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             var sortable = e.attr('')
3054             
3055             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3056                 e.addClass('glyphicon', 'glyphicon-arrow-up');
3057             }
3058             
3059             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3060                 e.addClass('glyphicon', 'glyphicon-arrow-down');
3061             }
3062         });
3063         
3064         var tbody = this.el.select('tbody', true).first();
3065         
3066         var renders = [];
3067         
3068         if(this.store.getCount() > 0){
3069             this.store.data.each(function(d){
3070                 var row = {
3071                     tag : 'tr',
3072                     cn : []
3073                 };
3074                 
3075                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3076                     var renderer = cm.getRenderer(i);
3077                     var config = cm.config[i];
3078                     var value = '';
3079                     var id = Roo.id();
3080                     
3081                     if(typeof(renderer) !== 'undefined'){
3082                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3083                     }
3084                     
3085                     if(typeof(value) === 'object'){
3086                         renders.push({
3087                             id : id,
3088                             cfg : value 
3089                         })
3090                     }
3091                     
3092                     var td = {
3093                         tag: 'td',
3094                         id: id,
3095                         html: (typeof(value) === 'object') ? '' : value
3096                     };
3097                     
3098                     if(typeof(config.width) != 'undefined'){
3099                         td.width = config.width;
3100                     }
3101                     
3102                     row.cn.push(td);
3103                    
3104                 }
3105                 
3106                 tbody.createChild(row);
3107                 
3108             });
3109         }
3110         
3111         
3112         if(renders.length){
3113             var _this = this;
3114             Roo.each(renders, function(r){
3115                 _this.renderColumn(r);
3116             })
3117         }
3118 //        
3119 //        if(this.loadMask){
3120 //            this.maskEl.hide();
3121 //        }
3122     },
3123     
3124     onBeforeLoad : function()
3125     {
3126         Roo.log('ds onBeforeLoad');
3127         
3128         this.clear();
3129         
3130 //        if(this.loadMask){
3131 //            this.maskEl.show();
3132 //        }
3133     },
3134     
3135     clear : function()
3136     {
3137         this.el.select('tbody', true).first().dom.innerHTML = '';
3138     },
3139     
3140     getSelectionModel : function(){
3141         if(!this.selModel){
3142             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3143         }
3144         return this.selModel;
3145     },
3146     
3147     renderColumn : function(r)
3148     {
3149         var _this = this;
3150         r.cfg.render(Roo.get(r.id));
3151         
3152         if(r.cfg.cn){
3153             Roo.each(r.cfg.cn, function(c){
3154                 var child = {
3155                     id: r.id,
3156                     cfg: c
3157                 }
3158                 _this.renderColumn(child);
3159             })
3160         }
3161     }
3162    
3163 });
3164
3165  
3166
3167  /*
3168  * - LGPL
3169  *
3170  * table cell
3171  * 
3172  */
3173
3174 /**
3175  * @class Roo.bootstrap.TableCell
3176  * @extends Roo.bootstrap.Component
3177  * Bootstrap TableCell class
3178  * @cfg {String} html cell contain text
3179  * @cfg {String} cls cell class
3180  * @cfg {String} tag cell tag (td|th) default td
3181  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3182  * @cfg {String} align Aligns the content in a cell
3183  * @cfg {String} axis Categorizes cells
3184  * @cfg {String} bgcolor Specifies the background color of a cell
3185  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3186  * @cfg {Number} colspan Specifies the number of columns a cell should span
3187  * @cfg {String} headers Specifies one or more header cells a cell is related to
3188  * @cfg {Number} height Sets the height of a cell
3189  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3190  * @cfg {Number} rowspan Sets the number of rows a cell should span
3191  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3192  * @cfg {String} valign Vertical aligns the content in a cell
3193  * @cfg {Number} width Specifies the width of a cell
3194  * 
3195  * @constructor
3196  * Create a new TableCell
3197  * @param {Object} config The config object
3198  */
3199
3200 Roo.bootstrap.TableCell = function(config){
3201     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3202 };
3203
3204 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3205     
3206     html: false,
3207     cls: false,
3208     tag: false,
3209     abbr: false,
3210     align: false,
3211     axis: false,
3212     bgcolor: false,
3213     charoff: false,
3214     colspan: false,
3215     headers: false,
3216     height: false,
3217     nowrap: false,
3218     rowspan: false,
3219     scope: false,
3220     valign: false,
3221     width: false,
3222     
3223     
3224     getAutoCreate : function(){
3225         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3226         
3227         cfg = {
3228             tag: 'td'
3229         }
3230         
3231         if(this.tag){
3232             cfg.tag = this.tag;
3233         }
3234         
3235         if (this.html) {
3236             cfg.html=this.html
3237         }
3238         if (this.cls) {
3239             cfg.cls=this.cls
3240         }
3241         if (this.abbr) {
3242             cfg.abbr=this.abbr
3243         }
3244         if (this.align) {
3245             cfg.align=this.align
3246         }
3247         if (this.axis) {
3248             cfg.axis=this.axis
3249         }
3250         if (this.bgcolor) {
3251             cfg.bgcolor=this.bgcolor
3252         }
3253         if (this.charoff) {
3254             cfg.charoff=this.charoff
3255         }
3256         if (this.colspan) {
3257             cfg.colspan=this.colspan
3258         }
3259         if (this.headers) {
3260             cfg.headers=this.headers
3261         }
3262         if (this.height) {
3263             cfg.height=this.height
3264         }
3265         if (this.nowrap) {
3266             cfg.nowrap=this.nowrap
3267         }
3268         if (this.rowspan) {
3269             cfg.rowspan=this.rowspan
3270         }
3271         if (this.scope) {
3272             cfg.scope=this.scope
3273         }
3274         if (this.valign) {
3275             cfg.valign=this.valign
3276         }
3277         if (this.width) {
3278             cfg.width=this.width
3279         }
3280         
3281         
3282         return cfg;
3283     }
3284    
3285 });
3286
3287  
3288
3289  /*
3290  * - LGPL
3291  *
3292  * table row
3293  * 
3294  */
3295
3296 /**
3297  * @class Roo.bootstrap.TableRow
3298  * @extends Roo.bootstrap.Component
3299  * Bootstrap TableRow class
3300  * @cfg {String} cls row class
3301  * @cfg {String} align Aligns the content in a table row
3302  * @cfg {String} bgcolor Specifies a background color for a table row
3303  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3304  * @cfg {String} valign Vertical aligns the content in a table row
3305  * 
3306  * @constructor
3307  * Create a new TableRow
3308  * @param {Object} config The config object
3309  */
3310
3311 Roo.bootstrap.TableRow = function(config){
3312     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3313 };
3314
3315 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3316     
3317     cls: false,
3318     align: false,
3319     bgcolor: false,
3320     charoff: false,
3321     valign: false,
3322     
3323     getAutoCreate : function(){
3324         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3325         
3326         cfg = {
3327             tag: 'tr'
3328         }
3329             
3330         if(this.cls){
3331             cfg.cls = this.cls;
3332         }
3333         if(this.align){
3334             cfg.align = this.align;
3335         }
3336         if(this.bgcolor){
3337             cfg.bgcolor = this.bgcolor;
3338         }
3339         if(this.charoff){
3340             cfg.charoff = this.charoff;
3341         }
3342         if(this.valign){
3343             cfg.valign = this.valign;
3344         }
3345         
3346         return cfg;
3347     }
3348    
3349 });
3350
3351  
3352
3353  /*
3354  * - LGPL
3355  *
3356  * table body
3357  * 
3358  */
3359
3360 /**
3361  * @class Roo.bootstrap.TableBody
3362  * @extends Roo.bootstrap.Component
3363  * Bootstrap TableBody class
3364  * @cfg {String} cls element class
3365  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3366  * @cfg {String} align Aligns the content inside the element
3367  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3368  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3369  * 
3370  * @constructor
3371  * Create a new TableBody
3372  * @param {Object} config The config object
3373  */
3374
3375 Roo.bootstrap.TableBody = function(config){
3376     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3377 };
3378
3379 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3380     
3381     cls: false,
3382     tag: false,
3383     align: false,
3384     charoff: false,
3385     valign: false,
3386     
3387     getAutoCreate : function(){
3388         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3389         
3390         cfg = {
3391             tag: 'tbody'
3392         }
3393             
3394         if (this.cls) {
3395             cfg.cls=this.cls
3396         }
3397         if(this.tag){
3398             cfg.tag = this.tag;
3399         }
3400         
3401         if(this.align){
3402             cfg.align = this.align;
3403         }
3404         if(this.charoff){
3405             cfg.charoff = this.charoff;
3406         }
3407         if(this.valign){
3408             cfg.valign = this.valign;
3409         }
3410         
3411         return cfg;
3412     }
3413     
3414     
3415 //    initEvents : function()
3416 //    {
3417 //        
3418 //        if(!this.store){
3419 //            return;
3420 //        }
3421 //        
3422 //        this.store = Roo.factory(this.store, Roo.data);
3423 //        this.store.on('load', this.onLoad, this);
3424 //        
3425 //        this.store.load();
3426 //        
3427 //    },
3428 //    
3429 //    onLoad: function () 
3430 //    {   
3431 //        this.fireEvent('load', this);
3432 //    }
3433 //    
3434 //   
3435 });
3436
3437  
3438
3439  /*
3440  * Based on:
3441  * Ext JS Library 1.1.1
3442  * Copyright(c) 2006-2007, Ext JS, LLC.
3443  *
3444  * Originally Released Under LGPL - original licence link has changed is not relivant.
3445  *
3446  * Fork - LGPL
3447  * <script type="text/javascript">
3448  */
3449
3450 // as we use this in bootstrap.
3451 Roo.namespace('Roo.form');
3452  /**
3453  * @class Roo.form.Action
3454  * Internal Class used to handle form actions
3455  * @constructor
3456  * @param {Roo.form.BasicForm} el The form element or its id
3457  * @param {Object} config Configuration options
3458  */
3459
3460  
3461  
3462 // define the action interface
3463 Roo.form.Action = function(form, options){
3464     this.form = form;
3465     this.options = options || {};
3466 };
3467 /**
3468  * Client Validation Failed
3469  * @const 
3470  */
3471 Roo.form.Action.CLIENT_INVALID = 'client';
3472 /**
3473  * Server Validation Failed
3474  * @const 
3475  */
3476 Roo.form.Action.SERVER_INVALID = 'server';
3477  /**
3478  * Connect to Server Failed
3479  * @const 
3480  */
3481 Roo.form.Action.CONNECT_FAILURE = 'connect';
3482 /**
3483  * Reading Data from Server Failed
3484  * @const 
3485  */
3486 Roo.form.Action.LOAD_FAILURE = 'load';
3487
3488 Roo.form.Action.prototype = {
3489     type : 'default',
3490     failureType : undefined,
3491     response : undefined,
3492     result : undefined,
3493
3494     // interface method
3495     run : function(options){
3496
3497     },
3498
3499     // interface method
3500     success : function(response){
3501
3502     },
3503
3504     // interface method
3505     handleResponse : function(response){
3506
3507     },
3508
3509     // default connection failure
3510     failure : function(response){
3511         
3512         this.response = response;
3513         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3514         this.form.afterAction(this, false);
3515     },
3516
3517     processResponse : function(response){
3518         this.response = response;
3519         if(!response.responseText){
3520             return true;
3521         }
3522         this.result = this.handleResponse(response);
3523         return this.result;
3524     },
3525
3526     // utility functions used internally
3527     getUrl : function(appendParams){
3528         var url = this.options.url || this.form.url || this.form.el.dom.action;
3529         if(appendParams){
3530             var p = this.getParams();
3531             if(p){
3532                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3533             }
3534         }
3535         return url;
3536     },
3537
3538     getMethod : function(){
3539         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3540     },
3541
3542     getParams : function(){
3543         var bp = this.form.baseParams;
3544         var p = this.options.params;
3545         if(p){
3546             if(typeof p == "object"){
3547                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3548             }else if(typeof p == 'string' && bp){
3549                 p += '&' + Roo.urlEncode(bp);
3550             }
3551         }else if(bp){
3552             p = Roo.urlEncode(bp);
3553         }
3554         return p;
3555     },
3556
3557     createCallback : function(){
3558         return {
3559             success: this.success,
3560             failure: this.failure,
3561             scope: this,
3562             timeout: (this.form.timeout*1000),
3563             upload: this.form.fileUpload ? this.success : undefined
3564         };
3565     }
3566 };
3567
3568 Roo.form.Action.Submit = function(form, options){
3569     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3570 };
3571
3572 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3573     type : 'submit',
3574
3575     haveProgress : false,
3576     uploadComplete : false,
3577     
3578     // uploadProgress indicator.
3579     uploadProgress : function()
3580     {
3581         if (!this.form.progressUrl) {
3582             return;
3583         }
3584         
3585         if (!this.haveProgress) {
3586             Roo.MessageBox.progress("Uploading", "Uploading");
3587         }
3588         if (this.uploadComplete) {
3589            Roo.MessageBox.hide();
3590            return;
3591         }
3592         
3593         this.haveProgress = true;
3594    
3595         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3596         
3597         var c = new Roo.data.Connection();
3598         c.request({
3599             url : this.form.progressUrl,
3600             params: {
3601                 id : uid
3602             },
3603             method: 'GET',
3604             success : function(req){
3605                //console.log(data);
3606                 var rdata = false;
3607                 var edata;
3608                 try  {
3609                    rdata = Roo.decode(req.responseText)
3610                 } catch (e) {
3611                     Roo.log("Invalid data from server..");
3612                     Roo.log(edata);
3613                     return;
3614                 }
3615                 if (!rdata || !rdata.success) {
3616                     Roo.log(rdata);
3617                     Roo.MessageBox.alert(Roo.encode(rdata));
3618                     return;
3619                 }
3620                 var data = rdata.data;
3621                 
3622                 if (this.uploadComplete) {
3623                    Roo.MessageBox.hide();
3624                    return;
3625                 }
3626                    
3627                 if (data){
3628                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3629                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3630                     );
3631                 }
3632                 this.uploadProgress.defer(2000,this);
3633             },
3634        
3635             failure: function(data) {
3636                 Roo.log('progress url failed ');
3637                 Roo.log(data);
3638             },
3639             scope : this
3640         });
3641            
3642     },
3643     
3644     
3645     run : function()
3646     {
3647         // run get Values on the form, so it syncs any secondary forms.
3648         this.form.getValues();
3649         
3650         var o = this.options;
3651         var method = this.getMethod();
3652         var isPost = method == 'POST';
3653         if(o.clientValidation === false || this.form.isValid()){
3654             
3655             if (this.form.progressUrl) {
3656                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3657                     (new Date() * 1) + '' + Math.random());
3658                     
3659             } 
3660             
3661             
3662             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3663                 form:this.form.el.dom,
3664                 url:this.getUrl(!isPost),
3665                 method: method,
3666                 params:isPost ? this.getParams() : null,
3667                 isUpload: this.form.fileUpload
3668             }));
3669             
3670             this.uploadProgress();
3671
3672         }else if (o.clientValidation !== false){ // client validation failed
3673             this.failureType = Roo.form.Action.CLIENT_INVALID;
3674             this.form.afterAction(this, false);
3675         }
3676     },
3677
3678     success : function(response)
3679     {
3680         this.uploadComplete= true;
3681         if (this.haveProgress) {
3682             Roo.MessageBox.hide();
3683         }
3684         
3685         
3686         var result = this.processResponse(response);
3687         if(result === true || result.success){
3688             this.form.afterAction(this, true);
3689             return;
3690         }
3691         if(result.errors){
3692             this.form.markInvalid(result.errors);
3693             this.failureType = Roo.form.Action.SERVER_INVALID;
3694         }
3695         this.form.afterAction(this, false);
3696     },
3697     failure : function(response)
3698     {
3699         this.uploadComplete= true;
3700         if (this.haveProgress) {
3701             Roo.MessageBox.hide();
3702         }
3703         
3704         this.response = response;
3705         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3706         this.form.afterAction(this, false);
3707     },
3708     
3709     handleResponse : function(response){
3710         if(this.form.errorReader){
3711             var rs = this.form.errorReader.read(response);
3712             var errors = [];
3713             if(rs.records){
3714                 for(var i = 0, len = rs.records.length; i < len; i++) {
3715                     var r = rs.records[i];
3716                     errors[i] = r.data;
3717                 }
3718             }
3719             if(errors.length < 1){
3720                 errors = null;
3721             }
3722             return {
3723                 success : rs.success,
3724                 errors : errors
3725             };
3726         }
3727         var ret = false;
3728         try {
3729             ret = Roo.decode(response.responseText);
3730         } catch (e) {
3731             ret = {
3732                 success: false,
3733                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3734                 errors : []
3735             };
3736         }
3737         return ret;
3738         
3739     }
3740 });
3741
3742
3743 Roo.form.Action.Load = function(form, options){
3744     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3745     this.reader = this.form.reader;
3746 };
3747
3748 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3749     type : 'load',
3750
3751     run : function(){
3752         
3753         Roo.Ajax.request(Roo.apply(
3754                 this.createCallback(), {
3755                     method:this.getMethod(),
3756                     url:this.getUrl(false),
3757                     params:this.getParams()
3758         }));
3759     },
3760
3761     success : function(response){
3762         
3763         var result = this.processResponse(response);
3764         if(result === true || !result.success || !result.data){
3765             this.failureType = Roo.form.Action.LOAD_FAILURE;
3766             this.form.afterAction(this, false);
3767             return;
3768         }
3769         this.form.clearInvalid();
3770         this.form.setValues(result.data);
3771         this.form.afterAction(this, true);
3772     },
3773
3774     handleResponse : function(response){
3775         if(this.form.reader){
3776             var rs = this.form.reader.read(response);
3777             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3778             return {
3779                 success : rs.success,
3780                 data : data
3781             };
3782         }
3783         return Roo.decode(response.responseText);
3784     }
3785 });
3786
3787 Roo.form.Action.ACTION_TYPES = {
3788     'load' : Roo.form.Action.Load,
3789     'submit' : Roo.form.Action.Submit
3790 };/*
3791  * - LGPL
3792  *
3793  * form
3794  * 
3795  */
3796
3797 /**
3798  * @class Roo.bootstrap.Form
3799  * @extends Roo.bootstrap.Component
3800  * Bootstrap Form class
3801  * @cfg {String} method  GET | POST (default POST)
3802  * @cfg {String} labelAlign top | left (default top)
3803   * @cfg {String} align left  | right - for navbars
3804
3805  * 
3806  * @constructor
3807  * Create a new Form
3808  * @param {Object} config The config object
3809  */
3810
3811
3812 Roo.bootstrap.Form = function(config){
3813     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3814     this.addEvents({
3815         /**
3816          * @event clientvalidation
3817          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3818          * @param {Form} this
3819          * @param {Boolean} valid true if the form has passed client-side validation
3820          */
3821         clientvalidation: true,
3822         /**
3823          * @event beforeaction
3824          * Fires before any action is performed. Return false to cancel the action.
3825          * @param {Form} this
3826          * @param {Action} action The action to be performed
3827          */
3828         beforeaction: true,
3829         /**
3830          * @event actionfailed
3831          * Fires when an action fails.
3832          * @param {Form} this
3833          * @param {Action} action The action that failed
3834          */
3835         actionfailed : true,
3836         /**
3837          * @event actioncomplete
3838          * Fires when an action is completed.
3839          * @param {Form} this
3840          * @param {Action} action The action that completed
3841          */
3842         actioncomplete : true
3843     });
3844     
3845 };
3846
3847 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3848       
3849      /**
3850      * @cfg {String} method
3851      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3852      */
3853     method : 'POST',
3854     /**
3855      * @cfg {String} url
3856      * The URL to use for form actions if one isn't supplied in the action options.
3857      */
3858     /**
3859      * @cfg {Boolean} fileUpload
3860      * Set to true if this form is a file upload.
3861      */
3862      
3863     /**
3864      * @cfg {Object} baseParams
3865      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3866      */
3867       
3868     /**
3869      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3870      */
3871     timeout: 30,
3872     /**
3873      * @cfg {Sting} align (left|right) for navbar forms
3874      */
3875     align : 'left',
3876
3877     // private
3878     activeAction : null,
3879  
3880     /**
3881      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3882      * element by passing it or its id or mask the form itself by passing in true.
3883      * @type Mixed
3884      */
3885     waitMsgTarget : false,
3886     
3887      
3888     
3889     /**
3890      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3891      * element by passing it or its id or mask the form itself by passing in true.
3892      * @type Mixed
3893      */
3894     
3895     getAutoCreate : function(){
3896         
3897         var cfg = {
3898             tag: 'form',
3899             method : this.method || 'POST',
3900             id : this.id || Roo.id(),
3901             cls : ''
3902         }
3903         if (this.parent().xtype.match(/^Nav/)) {
3904             cfg.cls = 'navbar-form navbar-' + this.align;
3905             
3906         }
3907         
3908         if (this.labelAlign == 'left' ) {
3909             cfg.cls += ' form-horizontal';
3910         }
3911         
3912         
3913         return cfg;
3914     },
3915     initEvents : function()
3916     {
3917         this.el.on('submit', this.onSubmit, this);
3918         
3919         
3920     },
3921     // private
3922     onSubmit : function(e){
3923         e.stopEvent();
3924     },
3925     
3926      /**
3927      * Returns true if client-side validation on the form is successful.
3928      * @return Boolean
3929      */
3930     isValid : function(){
3931         var items = this.getItems();
3932         var valid = true;
3933         items.each(function(f){
3934            if(!f.validate()){
3935                valid = false;
3936                
3937            }
3938         });
3939         return valid;
3940     },
3941     /**
3942      * Returns true if any fields in this form have changed since their original load.
3943      * @return Boolean
3944      */
3945     isDirty : function(){
3946         var dirty = false;
3947         var items = this.getItems();
3948         items.each(function(f){
3949            if(f.isDirty()){
3950                dirty = true;
3951                return false;
3952            }
3953            return true;
3954         });
3955         return dirty;
3956     },
3957      /**
3958      * Performs a predefined action (submit or load) or custom actions you define on this form.
3959      * @param {String} actionName The name of the action type
3960      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3961      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3962      * accept other config options):
3963      * <pre>
3964 Property          Type             Description
3965 ----------------  ---------------  ----------------------------------------------------------------------------------
3966 url               String           The url for the action (defaults to the form's url)
3967 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3968 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3969 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3970                                    validate the form on the client (defaults to false)
3971      * </pre>
3972      * @return {BasicForm} this
3973      */
3974     doAction : function(action, options){
3975         if(typeof action == 'string'){
3976             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3977         }
3978         if(this.fireEvent('beforeaction', this, action) !== false){
3979             this.beforeAction(action);
3980             action.run.defer(100, action);
3981         }
3982         return this;
3983     },
3984     
3985     // private
3986     beforeAction : function(action){
3987         var o = action.options;
3988         
3989         // not really supported yet.. ??
3990         
3991         //if(this.waitMsgTarget === true){
3992             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3993         //}else if(this.waitMsgTarget){
3994         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3995         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3996         //}else {
3997         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3998        // }
3999          
4000     },
4001
4002     // private
4003     afterAction : function(action, success){
4004         this.activeAction = null;
4005         var o = action.options;
4006         
4007         //if(this.waitMsgTarget === true){
4008             this.el.unmask();
4009         //}else if(this.waitMsgTarget){
4010         //    this.waitMsgTarget.unmask();
4011         //}else{
4012         //    Roo.MessageBox.updateProgress(1);
4013         //    Roo.MessageBox.hide();
4014        // }
4015         // 
4016         if(success){
4017             if(o.reset){
4018                 this.reset();
4019             }
4020             Roo.callback(o.success, o.scope, [this, action]);
4021             this.fireEvent('actioncomplete', this, action);
4022             
4023         }else{
4024             
4025             // failure condition..
4026             // we have a scenario where updates need confirming.
4027             // eg. if a locking scenario exists..
4028             // we look for { errors : { needs_confirm : true }} in the response.
4029             if (
4030                 (typeof(action.result) != 'undefined')  &&
4031                 (typeof(action.result.errors) != 'undefined')  &&
4032                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4033            ){
4034                 var _t = this;
4035                 Roo.log("not supported yet");
4036                  /*
4037                 
4038                 Roo.MessageBox.confirm(
4039                     "Change requires confirmation",
4040                     action.result.errorMsg,
4041                     function(r) {
4042                         if (r != 'yes') {
4043                             return;
4044                         }
4045                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4046                     }
4047                     
4048                 );
4049                 */
4050                 
4051                 
4052                 return;
4053             }
4054             
4055             Roo.callback(o.failure, o.scope, [this, action]);
4056             // show an error message if no failed handler is set..
4057             if (!this.hasListener('actionfailed')) {
4058                 Roo.log("need to add dialog support");
4059                 /*
4060                 Roo.MessageBox.alert("Error",
4061                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4062                         action.result.errorMsg :
4063                         "Saving Failed, please check your entries or try again"
4064                 );
4065                 */
4066             }
4067             
4068             this.fireEvent('actionfailed', this, action);
4069         }
4070         
4071     },
4072     /**
4073      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4074      * @param {String} id The value to search for
4075      * @return Field
4076      */
4077     findField : function(id){
4078         var items = this.getItems();
4079         var field = items.get(id);
4080         if(!field){
4081              items.each(function(f){
4082                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4083                     field = f;
4084                     return false;
4085                 }
4086                 return true;
4087             });
4088         }
4089         return field || null;
4090     },
4091      /**
4092      * Mark fields in this form invalid in bulk.
4093      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4094      * @return {BasicForm} this
4095      */
4096     markInvalid : function(errors){
4097         if(errors instanceof Array){
4098             for(var i = 0, len = errors.length; i < len; i++){
4099                 var fieldError = errors[i];
4100                 var f = this.findField(fieldError.id);
4101                 if(f){
4102                     f.markInvalid(fieldError.msg);
4103                 }
4104             }
4105         }else{
4106             var field, id;
4107             for(id in errors){
4108                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4109                     field.markInvalid(errors[id]);
4110                 }
4111             }
4112         }
4113         //Roo.each(this.childForms || [], function (f) {
4114         //    f.markInvalid(errors);
4115         //});
4116         
4117         return this;
4118     },
4119
4120     /**
4121      * Set values for fields in this form in bulk.
4122      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4123      * @return {BasicForm} this
4124      */
4125     setValues : function(values){
4126         if(values instanceof Array){ // array of objects
4127             for(var i = 0, len = values.length; i < len; i++){
4128                 var v = values[i];
4129                 var f = this.findField(v.id);
4130                 if(f){
4131                     f.setValue(v.value);
4132                     if(this.trackResetOnLoad){
4133                         f.originalValue = f.getValue();
4134                     }
4135                 }
4136             }
4137         }else{ // object hash
4138             var field, id;
4139             for(id in values){
4140                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4141                     
4142                     if (field.setFromData && 
4143                         field.valueField && 
4144                         field.displayField &&
4145                         // combos' with local stores can 
4146                         // be queried via setValue()
4147                         // to set their value..
4148                         (field.store && !field.store.isLocal)
4149                         ) {
4150                         // it's a combo
4151                         var sd = { };
4152                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4153                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4154                         field.setFromData(sd);
4155                         
4156                     } else {
4157                         field.setValue(values[id]);
4158                     }
4159                     
4160                     
4161                     if(this.trackResetOnLoad){
4162                         field.originalValue = field.getValue();
4163                     }
4164                 }
4165             }
4166         }
4167          
4168         //Roo.each(this.childForms || [], function (f) {
4169         //    f.setValues(values);
4170         //});
4171                 
4172         return this;
4173     },
4174
4175     /**
4176      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4177      * they are returned as an array.
4178      * @param {Boolean} asString
4179      * @return {Object}
4180      */
4181     getValues : function(asString){
4182         //if (this.childForms) {
4183             // copy values from the child forms
4184         //    Roo.each(this.childForms, function (f) {
4185         //        this.setValues(f.getValues());
4186         //    }, this);
4187         //}
4188         
4189         
4190         
4191         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4192         if(asString === true){
4193             return fs;
4194         }
4195         return Roo.urlDecode(fs);
4196     },
4197     
4198     /**
4199      * Returns the fields in this form as an object with key/value pairs. 
4200      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4201      * @return {Object}
4202      */
4203     getFieldValues : function(with_hidden)
4204     {
4205         var items = this.getItems();
4206         var ret = {};
4207         items.each(function(f){
4208             if (!f.getName()) {
4209                 return;
4210             }
4211             var v = f.getValue();
4212             if (f.inputType =='radio') {
4213                 if (typeof(ret[f.getName()]) == 'undefined') {
4214                     ret[f.getName()] = ''; // empty..
4215                 }
4216                 
4217                 if (!f.el.dom.checked) {
4218                     return;
4219                     
4220                 }
4221                 v = f.el.dom.value;
4222                 
4223             }
4224             
4225             // not sure if this supported any more..
4226             if ((typeof(v) == 'object') && f.getRawValue) {
4227                 v = f.getRawValue() ; // dates..
4228             }
4229             // combo boxes where name != hiddenName...
4230             if (f.name != f.getName()) {
4231                 ret[f.name] = f.getRawValue();
4232             }
4233             ret[f.getName()] = v;
4234         });
4235         
4236         return ret;
4237     },
4238
4239     /**
4240      * Clears all invalid messages in this form.
4241      * @return {BasicForm} this
4242      */
4243     clearInvalid : function(){
4244         var items = this.getItems();
4245         
4246         items.each(function(f){
4247            f.clearInvalid();
4248         });
4249         
4250         
4251         
4252         return this;
4253     },
4254
4255     /**
4256      * Resets this form.
4257      * @return {BasicForm} this
4258      */
4259     reset : function(){
4260         var items = this.getItems();
4261         items.each(function(f){
4262             f.reset();
4263         });
4264         
4265         Roo.each(this.childForms || [], function (f) {
4266             f.reset();
4267         });
4268        
4269         
4270         return this;
4271     },
4272     getItems : function()
4273     {
4274         var r=new Roo.util.MixedCollection(false, function(o){
4275             return o.id || (o.id = Roo.id());
4276         });
4277         var iter = function(el) {
4278             if (el.inputEl) {
4279                 r.add(el);
4280             }
4281             if (!el.items) {
4282                 return;
4283             }
4284             Roo.each(el.items,function(e) {
4285                 iter(e);
4286             });
4287             
4288             
4289         };
4290         iter(this);
4291         return r;
4292         
4293         
4294         
4295         
4296     }
4297     
4298 });
4299
4300  
4301 /*
4302  * Based on:
4303  * Ext JS Library 1.1.1
4304  * Copyright(c) 2006-2007, Ext JS, LLC.
4305  *
4306  * Originally Released Under LGPL - original licence link has changed is not relivant.
4307  *
4308  * Fork - LGPL
4309  * <script type="text/javascript">
4310  */
4311 /**
4312  * @class Roo.form.VTypes
4313  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4314  * @singleton
4315  */
4316 Roo.form.VTypes = function(){
4317     // closure these in so they are only created once.
4318     var alpha = /^[a-zA-Z_]+$/;
4319     var alphanum = /^[a-zA-Z0-9_]+$/;
4320     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4321     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4322
4323     // All these messages and functions are configurable
4324     return {
4325         /**
4326          * The function used to validate email addresses
4327          * @param {String} value The email address
4328          */
4329         'email' : function(v){
4330             return email.test(v);
4331         },
4332         /**
4333          * The error text to display when the email validation function returns false
4334          * @type String
4335          */
4336         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4337         /**
4338          * The keystroke filter mask to be applied on email input
4339          * @type RegExp
4340          */
4341         'emailMask' : /[a-z0-9_\.\-@]/i,
4342
4343         /**
4344          * The function used to validate URLs
4345          * @param {String} value The URL
4346          */
4347         'url' : function(v){
4348             return url.test(v);
4349         },
4350         /**
4351          * The error text to display when the url validation function returns false
4352          * @type String
4353          */
4354         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4355         
4356         /**
4357          * The function used to validate alpha values
4358          * @param {String} value The value
4359          */
4360         'alpha' : function(v){
4361             return alpha.test(v);
4362         },
4363         /**
4364          * The error text to display when the alpha validation function returns false
4365          * @type String
4366          */
4367         'alphaText' : 'This field should only contain letters and _',
4368         /**
4369          * The keystroke filter mask to be applied on alpha input
4370          * @type RegExp
4371          */
4372         'alphaMask' : /[a-z_]/i,
4373
4374         /**
4375          * The function used to validate alphanumeric values
4376          * @param {String} value The value
4377          */
4378         'alphanum' : function(v){
4379             return alphanum.test(v);
4380         },
4381         /**
4382          * The error text to display when the alphanumeric validation function returns false
4383          * @type String
4384          */
4385         'alphanumText' : 'This field should only contain letters, numbers and _',
4386         /**
4387          * The keystroke filter mask to be applied on alphanumeric input
4388          * @type RegExp
4389          */
4390         'alphanumMask' : /[a-z0-9_]/i
4391     };
4392 }();/*
4393  * - LGPL
4394  *
4395  * Input
4396  * 
4397  */
4398
4399 /**
4400  * @class Roo.bootstrap.Input
4401  * @extends Roo.bootstrap.Component
4402  * Bootstrap Input class
4403  * @cfg {Boolean} disabled is it disabled
4404  * @cfg {String} fieldLabel - the label associated
4405  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4406  * @cfg {String} name name of the input
4407  * @cfg {string} fieldLabel - the label associated
4408  * @cfg {string}  inputType - input / file submit ...
4409  * @cfg {string} placeholder - placeholder to put in text.
4410  * @cfg {string}  before - input group add on before
4411  * @cfg {string} after - input group add on after
4412  * @cfg {string} size - (lg|sm) or leave empty..
4413  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4414  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4415  * @cfg {Number} md colspan out of 12 for computer-sized screens
4416  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4417  * @cfg {string} value default value of the input
4418  * @cfg {Number} labelWidth set the width of label (0-12)
4419  * @cfg {String} labelAlign (top|left)
4420  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4421  * 
4422  * 
4423  * @constructor
4424  * Create a new Input
4425  * @param {Object} config The config object
4426  */
4427
4428 Roo.bootstrap.Input = function(config){
4429     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4430    
4431         this.addEvents({
4432             /**
4433              * @event focus
4434              * Fires when this field receives input focus.
4435              * @param {Roo.form.Field} this
4436              */
4437             focus : true,
4438             /**
4439              * @event blur
4440              * Fires when this field loses input focus.
4441              * @param {Roo.form.Field} this
4442              */
4443             blur : true,
4444             /**
4445              * @event specialkey
4446              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4447              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4448              * @param {Roo.form.Field} this
4449              * @param {Roo.EventObject} e The event object
4450              */
4451             specialkey : true,
4452             /**
4453              * @event change
4454              * Fires just before the field blurs if the field value has changed.
4455              * @param {Roo.form.Field} this
4456              * @param {Mixed} newValue The new value
4457              * @param {Mixed} oldValue The original value
4458              */
4459             change : true,
4460             /**
4461              * @event invalid
4462              * Fires after the field has been marked as invalid.
4463              * @param {Roo.form.Field} this
4464              * @param {String} msg The validation message
4465              */
4466             invalid : true,
4467             /**
4468              * @event valid
4469              * Fires after the field has been validated with no errors.
4470              * @param {Roo.form.Field} this
4471              */
4472             valid : true,
4473              /**
4474              * @event keyup
4475              * Fires after the key up
4476              * @param {Roo.form.Field} this
4477              * @param {Roo.EventObject}  e The event Object
4478              */
4479             keyup : true
4480         });
4481 };
4482
4483 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4484      /**
4485      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4486       automatic validation (defaults to "keyup").
4487      */
4488     validationEvent : "keyup",
4489      /**
4490      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4491      */
4492     validateOnBlur : true,
4493     /**
4494      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4495      */
4496     validationDelay : 250,
4497      /**
4498      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4499      */
4500     focusClass : "x-form-focus",  // not needed???
4501     
4502        
4503     /**
4504      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4505      */
4506     invalidClass : "has-error",
4507     
4508     /**
4509      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4510      */
4511     selectOnFocus : false,
4512     
4513      /**
4514      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4515      */
4516     maskRe : null,
4517        /**
4518      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4519      */
4520     vtype : null,
4521     
4522       /**
4523      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4524      */
4525     disableKeyFilter : false,
4526     
4527        /**
4528      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4529      */
4530     disabled : false,
4531      /**
4532      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4533      */
4534     allowBlank : true,
4535     /**
4536      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4537      */
4538     blankText : "This field is required",
4539     
4540      /**
4541      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4542      */
4543     minLength : 0,
4544     /**
4545      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4546      */
4547     maxLength : Number.MAX_VALUE,
4548     /**
4549      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4550      */
4551     minLengthText : "The minimum length for this field is {0}",
4552     /**
4553      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4554      */
4555     maxLengthText : "The maximum length for this field is {0}",
4556   
4557     
4558     /**
4559      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4560      * If available, this function will be called only after the basic validators all return true, and will be passed the
4561      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4562      */
4563     validator : null,
4564     /**
4565      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4566      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4567      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4568      */
4569     regex : null,
4570     /**
4571      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4572      */
4573     regexText : "",
4574     
4575     
4576     
4577     fieldLabel : '',
4578     inputType : 'text',
4579     
4580     name : false,
4581     placeholder: false,
4582     before : false,
4583     after : false,
4584     size : false,
4585     // private
4586     hasFocus : false,
4587     preventMark: false,
4588     isFormField : true,
4589     value : '',
4590     labelWidth : 2,
4591     labelAlign : false,
4592     readOnly : false,
4593     
4594     parentLabelAlign : function()
4595     {
4596         var parent = this;
4597         while (parent.parent()) {
4598             parent = parent.parent();
4599             if (typeof(parent.labelAlign) !='undefined') {
4600                 return parent.labelAlign;
4601             }
4602         }
4603         return 'left';
4604         
4605     },
4606     
4607     getAutoCreate : function(){
4608         
4609         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4610         
4611         var id = Roo.id();
4612         
4613         var cfg = {};
4614         
4615         if(this.inputType != 'hidden'){
4616             cfg.cls = 'form-group' //input-group
4617         }
4618         
4619         var input =  {
4620             tag: 'input',
4621             id : id,
4622             type : this.inputType,
4623             value : this.value,
4624             cls : 'form-control',
4625             placeholder : this.placeholder || ''
4626             
4627         };
4628         
4629         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4630             input.maxLength = this.maxLength;
4631         }
4632         
4633         if (this.disabled) {
4634             input.disabled=true;
4635         }
4636         
4637         if (this.readOnly) {
4638             input.readonly=true;
4639         }
4640         
4641         if (this.name) {
4642             input.name = this.name;
4643         }
4644         if (this.size) {
4645             input.cls += ' input-' + this.size;
4646         }
4647         var settings=this;
4648         ['xs','sm','md','lg'].map(function(size){
4649             if (settings[size]) {
4650                 cfg.cls += ' col-' + size + '-' + settings[size];
4651             }
4652         });
4653         
4654         var inputblock = input;
4655         
4656         if (this.before || this.after) {
4657             
4658             inputblock = {
4659                 cls : 'input-group',
4660                 cn :  [] 
4661             };
4662             if (this.before) {
4663                 inputblock.cn.push({
4664                     tag :'span',
4665                     cls : 'input-group-addon',
4666                     html : this.before
4667                 });
4668             }
4669             inputblock.cn.push(input);
4670             if (this.after) {
4671                 inputblock.cn.push({
4672                     tag :'span',
4673                     cls : 'input-group-addon',
4674                     html : this.after
4675                 });
4676             }
4677             
4678         };
4679         
4680         if (align ==='left' && this.fieldLabel.length) {
4681                 Roo.log("left and has label");
4682                 cfg.cn = [
4683                     
4684                     {
4685                         tag: 'label',
4686                         'for' :  id,
4687                         cls : 'control-label col-sm-' + this.labelWidth,
4688                         html : this.fieldLabel
4689                         
4690                     },
4691                     {
4692                         cls : "col-sm-" + (12 - this.labelWidth), 
4693                         cn: [
4694                             inputblock
4695                         ]
4696                     }
4697                     
4698                 ];
4699         } else if ( this.fieldLabel.length) {
4700                 Roo.log(" label");
4701                  cfg.cn = [
4702                    
4703                     {
4704                         tag: 'label',
4705                         //cls : 'input-group-addon',
4706                         html : this.fieldLabel
4707                         
4708                     },
4709                     
4710                     inputblock
4711                     
4712                 ];
4713
4714         } else {
4715             
4716                 Roo.log(" no label && no align");
4717                 cfg.cn = [
4718                     
4719                         inputblock
4720                     
4721                 ];
4722                 
4723                 
4724         };
4725         Roo.log('input-parentType: ' + this.parentType);
4726         
4727         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4728            cfg.cls += ' navbar-form';
4729            Roo.log(cfg);
4730         }
4731         
4732         return cfg;
4733         
4734     },
4735     /**
4736      * return the real input element.
4737      */
4738     inputEl: function ()
4739     {
4740         return this.el.select('input.form-control',true).first();
4741     },
4742     setDisabled : function(v)
4743     {
4744         var i  = this.inputEl().dom;
4745         if (!v) {
4746             i.removeAttribute('disabled');
4747             return;
4748             
4749         }
4750         i.setAttribute('disabled','true');
4751     },
4752     initEvents : function()
4753     {
4754         
4755         this.inputEl().on("keydown" , this.fireKey,  this);
4756         this.inputEl().on("focus", this.onFocus,  this);
4757         this.inputEl().on("blur", this.onBlur,  this);
4758         
4759         this.inputEl().relayEvent('keyup', this);
4760
4761         // reference to original value for reset
4762         this.originalValue = this.getValue();
4763         //Roo.form.TextField.superclass.initEvents.call(this);
4764         if(this.validationEvent == 'keyup'){
4765             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4766             this.inputEl().on('keyup', this.filterValidation, this);
4767         }
4768         else if(this.validationEvent !== false){
4769             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4770         }
4771         
4772         if(this.selectOnFocus){
4773             this.on("focus", this.preFocus, this);
4774             
4775         }
4776         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4777             this.inputEl().on("keypress", this.filterKeys, this);
4778         }
4779        /* if(this.grow){
4780             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4781             this.el.on("click", this.autoSize,  this);
4782         }
4783         */
4784         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4785             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4786         }
4787         
4788     },
4789     filterValidation : function(e){
4790         if(!e.isNavKeyPress()){
4791             this.validationTask.delay(this.validationDelay);
4792         }
4793     },
4794      /**
4795      * Validates the field value
4796      * @return {Boolean} True if the value is valid, else false
4797      */
4798     validate : function(){
4799         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4800         if(this.disabled || this.validateValue(this.getRawValue())){
4801             this.clearInvalid();
4802             return true;
4803         }
4804         return false;
4805     },
4806     
4807     
4808     /**
4809      * Validates a value according to the field's validation rules and marks the field as invalid
4810      * if the validation fails
4811      * @param {Mixed} value The value to validate
4812      * @return {Boolean} True if the value is valid, else false
4813      */
4814     validateValue : function(value){
4815         if(value.length < 1)  { // if it's blank
4816              if(this.allowBlank){
4817                 this.clearInvalid();
4818                 return true;
4819              }else{
4820                 this.markInvalid(this.blankText);
4821                 return false;
4822              }
4823         }
4824         if(value.length < this.minLength){
4825             this.markInvalid(String.format(this.minLengthText, this.minLength));
4826             return false;
4827         }
4828         if(value.length > this.maxLength){
4829             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4830             return false;
4831         }
4832         if(this.vtype){
4833             var vt = Roo.form.VTypes;
4834             if(!vt[this.vtype](value, this)){
4835                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4836                 return false;
4837             }
4838         }
4839         if(typeof this.validator == "function"){
4840             var msg = this.validator(value);
4841             if(msg !== true){
4842                 this.markInvalid(msg);
4843                 return false;
4844             }
4845         }
4846         if(this.regex && !this.regex.test(value)){
4847             this.markInvalid(this.regexText);
4848             return false;
4849         }
4850         return true;
4851     },
4852
4853     
4854     
4855      // private
4856     fireKey : function(e){
4857         //Roo.log('field ' + e.getKey());
4858         if(e.isNavKeyPress()){
4859             this.fireEvent("specialkey", this, e);
4860         }
4861     },
4862     focus : function (selectText){
4863         if(this.rendered){
4864             this.inputEl().focus();
4865             if(selectText === true){
4866                 this.inputEl().dom.select();
4867             }
4868         }
4869         return this;
4870     } ,
4871     
4872     onFocus : function(){
4873         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4874            // this.el.addClass(this.focusClass);
4875         }
4876         if(!this.hasFocus){
4877             this.hasFocus = true;
4878             this.startValue = this.getValue();
4879             this.fireEvent("focus", this);
4880         }
4881     },
4882     
4883     beforeBlur : Roo.emptyFn,
4884
4885     
4886     // private
4887     onBlur : function(){
4888         this.beforeBlur();
4889         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4890             //this.el.removeClass(this.focusClass);
4891         }
4892         this.hasFocus = false;
4893         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4894             this.validate();
4895         }
4896         var v = this.getValue();
4897         if(String(v) !== String(this.startValue)){
4898             this.fireEvent('change', this, v, this.startValue);
4899         }
4900         this.fireEvent("blur", this);
4901     },
4902     
4903     /**
4904      * Resets the current field value to the originally loaded value and clears any validation messages
4905      */
4906     reset : function(){
4907         this.setValue(this.originalValue);
4908         this.clearInvalid();
4909     },
4910      /**
4911      * Returns the name of the field
4912      * @return {Mixed} name The name field
4913      */
4914     getName: function(){
4915         return this.name;
4916     },
4917      /**
4918      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4919      * @return {Mixed} value The field value
4920      */
4921     getValue : function(){
4922         return this.inputEl().getValue();
4923     },
4924     /**
4925      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4926      * @return {Mixed} value The field value
4927      */
4928     getRawValue : function(){
4929         var v = this.inputEl().getValue();
4930         
4931         return v;
4932     },
4933     
4934     /**
4935      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4936      * @param {Mixed} value The value to set
4937      */
4938     setRawValue : function(v){
4939         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4940     },
4941     
4942     selectText : function(start, end){
4943         var v = this.getRawValue();
4944         if(v.length > 0){
4945             start = start === undefined ? 0 : start;
4946             end = end === undefined ? v.length : end;
4947             var d = this.inputEl().dom;
4948             if(d.setSelectionRange){
4949                 d.setSelectionRange(start, end);
4950             }else if(d.createTextRange){
4951                 var range = d.createTextRange();
4952                 range.moveStart("character", start);
4953                 range.moveEnd("character", v.length-end);
4954                 range.select();
4955             }
4956         }
4957     },
4958     
4959     /**
4960      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4961      * @param {Mixed} value The value to set
4962      */
4963     setValue : function(v){
4964         this.value = v;
4965         if(this.rendered){
4966             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4967             this.validate();
4968         }
4969     },
4970     
4971     /*
4972     processValue : function(value){
4973         if(this.stripCharsRe){
4974             var newValue = value.replace(this.stripCharsRe, '');
4975             if(newValue !== value){
4976                 this.setRawValue(newValue);
4977                 return newValue;
4978             }
4979         }
4980         return value;
4981     },
4982   */
4983     preFocus : function(){
4984         
4985         if(this.selectOnFocus){
4986             this.inputEl().dom.select();
4987         }
4988     },
4989     filterKeys : function(e){
4990         var k = e.getKey();
4991         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4992             return;
4993         }
4994         var c = e.getCharCode(), cc = String.fromCharCode(c);
4995         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4996             return;
4997         }
4998         if(!this.maskRe.test(cc)){
4999             e.stopEvent();
5000         }
5001     },
5002      /**
5003      * Clear any invalid styles/messages for this field
5004      */
5005     clearInvalid : function(){
5006         
5007         if(!this.el || this.preventMark){ // not rendered
5008             return;
5009         }
5010         this.el.removeClass(this.invalidClass);
5011         /*
5012         switch(this.msgTarget){
5013             case 'qtip':
5014                 this.el.dom.qtip = '';
5015                 break;
5016             case 'title':
5017                 this.el.dom.title = '';
5018                 break;
5019             case 'under':
5020                 if(this.errorEl){
5021                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5022                 }
5023                 break;
5024             case 'side':
5025                 if(this.errorIcon){
5026                     this.errorIcon.dom.qtip = '';
5027                     this.errorIcon.hide();
5028                     this.un('resize', this.alignErrorIcon, this);
5029                 }
5030                 break;
5031             default:
5032                 var t = Roo.getDom(this.msgTarget);
5033                 t.innerHTML = '';
5034                 t.style.display = 'none';
5035                 break;
5036         }
5037         */
5038         this.fireEvent('valid', this);
5039     },
5040      /**
5041      * Mark this field as invalid
5042      * @param {String} msg The validation message
5043      */
5044     markInvalid : function(msg){
5045         if(!this.el  || this.preventMark){ // not rendered
5046             return;
5047         }
5048         this.el.addClass(this.invalidClass);
5049         /*
5050         msg = msg || this.invalidText;
5051         switch(this.msgTarget){
5052             case 'qtip':
5053                 this.el.dom.qtip = msg;
5054                 this.el.dom.qclass = 'x-form-invalid-tip';
5055                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5056                     Roo.QuickTips.enable();
5057                 }
5058                 break;
5059             case 'title':
5060                 this.el.dom.title = msg;
5061                 break;
5062             case 'under':
5063                 if(!this.errorEl){
5064                     var elp = this.el.findParent('.x-form-element', 5, true);
5065                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5066                     this.errorEl.setWidth(elp.getWidth(true)-20);
5067                 }
5068                 this.errorEl.update(msg);
5069                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5070                 break;
5071             case 'side':
5072                 if(!this.errorIcon){
5073                     var elp = this.el.findParent('.x-form-element', 5, true);
5074                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5075                 }
5076                 this.alignErrorIcon();
5077                 this.errorIcon.dom.qtip = msg;
5078                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5079                 this.errorIcon.show();
5080                 this.on('resize', this.alignErrorIcon, this);
5081                 break;
5082             default:
5083                 var t = Roo.getDom(this.msgTarget);
5084                 t.innerHTML = msg;
5085                 t.style.display = this.msgDisplay;
5086                 break;
5087         }
5088         */
5089         this.fireEvent('invalid', this, msg);
5090     },
5091     // private
5092     SafariOnKeyDown : function(event)
5093     {
5094         // this is a workaround for a password hang bug on chrome/ webkit.
5095         
5096         var isSelectAll = false;
5097         
5098         if(this.inputEl().dom.selectionEnd > 0){
5099             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5100         }
5101         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5102             event.preventDefault();
5103             this.setValue('');
5104             return;
5105         }
5106         
5107         if(isSelectAll){ // backspace and delete key
5108             
5109             event.preventDefault();
5110             // this is very hacky as keydown always get's upper case.
5111             //
5112             var cc = String.fromCharCode(event.getCharCode());
5113             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5114             
5115         }
5116     },
5117     adjustWidth : function(tag, w){
5118         tag = tag.toLowerCase();
5119         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5120             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5121                 if(tag == 'input'){
5122                     return w + 2;
5123                 }
5124                 if(tag == 'textarea'){
5125                     return w-2;
5126                 }
5127             }else if(Roo.isOpera){
5128                 if(tag == 'input'){
5129                     return w + 2;
5130                 }
5131                 if(tag == 'textarea'){
5132                     return w-2;
5133                 }
5134             }
5135         }
5136         return w;
5137     }
5138     
5139 });
5140
5141  
5142 /*
5143  * - LGPL
5144  *
5145  * Input
5146  * 
5147  */
5148
5149 /**
5150  * @class Roo.bootstrap.TextArea
5151  * @extends Roo.bootstrap.Input
5152  * Bootstrap TextArea class
5153  * @cfg {Number} cols Specifies the visible width of a text area
5154  * @cfg {Number} rows Specifies the visible number of lines in a text area
5155  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5156  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5157  * @cfg {string} html text
5158  * 
5159  * @constructor
5160  * Create a new TextArea
5161  * @param {Object} config The config object
5162  */
5163
5164 Roo.bootstrap.TextArea = function(config){
5165     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5166    
5167 };
5168
5169 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5170      
5171     cols : false,
5172     rows : 5,
5173     readOnly : false,
5174     warp : 'soft',
5175     resize : false,
5176     value: false,
5177     html: false,
5178     
5179     getAutoCreate : function(){
5180         
5181         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5182         
5183         var id = Roo.id();
5184         
5185         var cfg = {};
5186         
5187         var input =  {
5188             tag: 'textarea',
5189             id : id,
5190             warp : this.warp,
5191             rows : this.rows,
5192             value : this.value || '',
5193             html: this.html || '',
5194             cls : 'form-control',
5195             placeholder : this.placeholder || '' 
5196             
5197         };
5198         
5199         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5200             input.maxLength = this.maxLength;
5201         }
5202         
5203         if(this.resize){
5204             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5205         }
5206         
5207         if(this.cols){
5208             input.cols = this.cols;
5209         }
5210         
5211         if (this.readOnly) {
5212             input.readonly = true;
5213         }
5214         
5215         if (this.name) {
5216             input.name = this.name;
5217         }
5218         
5219         if (this.size) {
5220             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5221         }
5222         
5223         var settings=this;
5224         ['xs','sm','md','lg'].map(function(size){
5225             if (settings[size]) {
5226                 cfg.cls += ' col-' + size + '-' + settings[size];
5227             }
5228         });
5229         
5230         var inputblock = input;
5231         
5232         if (this.before || this.after) {
5233             
5234             inputblock = {
5235                 cls : 'input-group',
5236                 cn :  [] 
5237             };
5238             if (this.before) {
5239                 inputblock.cn.push({
5240                     tag :'span',
5241                     cls : 'input-group-addon',
5242                     html : this.before
5243                 });
5244             }
5245             inputblock.cn.push(input);
5246             if (this.after) {
5247                 inputblock.cn.push({
5248                     tag :'span',
5249                     cls : 'input-group-addon',
5250                     html : this.after
5251                 });
5252             }
5253             
5254         }
5255         
5256         if (align ==='left' && this.fieldLabel.length) {
5257                 Roo.log("left and has label");
5258                 cfg.cn = [
5259                     
5260                     {
5261                         tag: 'label',
5262                         'for' :  id,
5263                         cls : 'control-label col-sm-' + this.labelWidth,
5264                         html : this.fieldLabel
5265                         
5266                     },
5267                     {
5268                         cls : "col-sm-" + (12 - this.labelWidth), 
5269                         cn: [
5270                             inputblock
5271                         ]
5272                     }
5273                     
5274                 ];
5275         } else if ( this.fieldLabel.length) {
5276                 Roo.log(" label");
5277                  cfg.cn = [
5278                    
5279                     {
5280                         tag: 'label',
5281                         //cls : 'input-group-addon',
5282                         html : this.fieldLabel
5283                         
5284                     },
5285                     
5286                     inputblock
5287                     
5288                 ];
5289
5290         } else {
5291             
5292                    Roo.log(" no label && no align");
5293                 cfg.cn = [
5294                     
5295                         inputblock
5296                     
5297                 ];
5298                 
5299                 
5300         }
5301         
5302         if (this.disabled) {
5303             input.disabled=true;
5304         }
5305         
5306         return cfg;
5307         
5308     },
5309     /**
5310      * return the real textarea element.
5311      */
5312     inputEl: function ()
5313     {
5314         return this.el.select('textarea.form-control',true).first();
5315     }
5316 });
5317
5318  
5319 /*
5320  * - LGPL
5321  *
5322  * trigger field - base class for combo..
5323  * 
5324  */
5325  
5326 /**
5327  * @class Roo.bootstrap.TriggerField
5328  * @extends Roo.bootstrap.Input
5329  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5330  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5331  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5332  * for which you can provide a custom implementation.  For example:
5333  * <pre><code>
5334 var trigger = new Roo.bootstrap.TriggerField();
5335 trigger.onTriggerClick = myTriggerFn;
5336 trigger.applyTo('my-field');
5337 </code></pre>
5338  *
5339  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5340  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5341  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5342  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5343  * @constructor
5344  * Create a new TriggerField.
5345  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5346  * to the base TextField)
5347  */
5348 Roo.bootstrap.TriggerField = function(config){
5349     this.mimicing = false;
5350     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5351 };
5352
5353 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5354     /**
5355      * @cfg {String} triggerClass A CSS class to apply to the trigger
5356      */
5357      /**
5358      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5359      */
5360     hideTrigger:false,
5361
5362     /** @cfg {Boolean} grow @hide */
5363     /** @cfg {Number} growMin @hide */
5364     /** @cfg {Number} growMax @hide */
5365
5366     /**
5367      * @hide 
5368      * @method
5369      */
5370     autoSize: Roo.emptyFn,
5371     // private
5372     monitorTab : true,
5373     // private
5374     deferHeight : true,
5375
5376     
5377     actionMode : 'wrap',
5378     
5379     
5380     
5381     getAutoCreate : function(){
5382        
5383         var parent = this.parent();
5384         
5385         var align = this.parentLabelAlign();
5386         
5387         var id = Roo.id();
5388         
5389         var cfg = {
5390             cls: 'form-group' //input-group
5391         };
5392         
5393         
5394         var input =  {
5395             tag: 'input',
5396             id : id,
5397             type : this.inputType,
5398             cls : 'form-control',
5399             autocomplete: 'off',
5400             placeholder : this.placeholder || '' 
5401             
5402         };
5403         if (this.name) {
5404             input.name = this.name;
5405         }
5406         if (this.size) {
5407             input.cls += ' input-' + this.size;
5408         }
5409         
5410         if (this.disabled) {
5411             input.disabled=true;
5412         }
5413         
5414         var inputblock = input;
5415         
5416         if (this.before || this.after) {
5417             
5418             inputblock = {
5419                 cls : 'input-group',
5420                 cn :  [] 
5421             };
5422             if (this.before) {
5423                 inputblock.cn.push({
5424                     tag :'span',
5425                     cls : 'input-group-addon',
5426                     html : this.before
5427                 });
5428             }
5429             inputblock.cn.push(input);
5430             if (this.after) {
5431                 inputblock.cn.push({
5432                     tag :'span',
5433                     cls : 'input-group-addon',
5434                     html : this.after
5435                 });
5436             }
5437             
5438         };
5439         
5440         var box = {
5441             tag: 'div',
5442             cn: [
5443                 {
5444                     tag: 'input',
5445                     type : 'hidden',
5446                     cls: 'form-hidden-field'
5447                 },
5448                 inputblock
5449             ]
5450             
5451         };
5452         
5453         if(this.multiple){
5454             Roo.log('multiple');
5455             
5456             box = {
5457                 tag: 'div',
5458                 cn: [
5459                     {
5460                         tag: 'input',
5461                         type : 'hidden',
5462                         cls: 'form-hidden-field'
5463                     },
5464                     {
5465                         tag: 'ul',
5466                         cls: 'select2-choices',
5467                         cn:[
5468                             {
5469                                 tag: 'li',
5470                                 cls: 'select2-search-field',
5471                                 cn: [
5472
5473                                     inputblock
5474                                 ]
5475                             }
5476                         ]
5477                     }
5478                 ]
5479             }
5480         };
5481         
5482         var combobox = {
5483             cls: 'select2-container input-group',
5484             cn: [
5485                 box,
5486                 {
5487                     tag: 'ul',
5488                     cls: 'typeahead typeahead-long dropdown-menu',
5489                     style: 'display:none'
5490                 }
5491             ]
5492         };
5493         
5494         if(!this.multiple){
5495             combobox.cn.push({
5496                 tag :'span',
5497                 cls : 'input-group-addon btn dropdown-toggle',
5498                 cn : [
5499                     {
5500                         tag: 'span',
5501                         cls: 'caret'
5502                     },
5503                     {
5504                         tag: 'span',
5505                         cls: 'combobox-clear',
5506                         cn  : [
5507                             {
5508                                 tag : 'i',
5509                                 cls: 'icon-remove'
5510                             }
5511                         ]
5512                     }
5513                 ]
5514
5515             })
5516         }
5517         
5518         if(this.multiple){
5519             combobox.cls += ' select2-container-multi';
5520         }
5521         
5522         if (align ==='left' && this.fieldLabel.length) {
5523             
5524                 Roo.log("left and has label");
5525                 cfg.cn = [
5526                     
5527                     {
5528                         tag: 'label',
5529                         'for' :  id,
5530                         cls : 'control-label col-sm-' + this.labelWidth,
5531                         html : this.fieldLabel
5532                         
5533                     },
5534                     {
5535                         cls : "col-sm-" + (12 - this.labelWidth), 
5536                         cn: [
5537                             combobox
5538                         ]
5539                     }
5540                     
5541                 ];
5542         } else if ( this.fieldLabel.length) {
5543                 Roo.log(" label");
5544                  cfg.cn = [
5545                    
5546                     {
5547                         tag: 'label',
5548                         //cls : 'input-group-addon',
5549                         html : this.fieldLabel
5550                         
5551                     },
5552                     
5553                     combobox
5554                     
5555                 ];
5556
5557         } else {
5558             
5559                 Roo.log(" no label && no align");
5560                 cfg = combobox
5561                      
5562                 
5563         }
5564          
5565         var settings=this;
5566         ['xs','sm','md','lg'].map(function(size){
5567             if (settings[size]) {
5568                 cfg.cls += ' col-' + size + '-' + settings[size];
5569             }
5570         });
5571         
5572         return cfg;
5573         
5574     },
5575     
5576     
5577     
5578     // private
5579     onResize : function(w, h){
5580 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5581 //        if(typeof w == 'number'){
5582 //            var x = w - this.trigger.getWidth();
5583 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5584 //            this.trigger.setStyle('left', x+'px');
5585 //        }
5586     },
5587
5588     // private
5589     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5590
5591     // private
5592     getResizeEl : function(){
5593         return this.inputEl();
5594     },
5595
5596     // private
5597     getPositionEl : function(){
5598         return this.inputEl();
5599     },
5600
5601     // private
5602     alignErrorIcon : function(){
5603         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5604     },
5605
5606     // private
5607     initEvents : function(){
5608         
5609         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5610         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5611         if(!this.multiple){
5612             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5613             if(this.hideTrigger){
5614                 this.trigger.setDisplayed(false);
5615             }
5616             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5617         }
5618         
5619         if(this.multiple){
5620             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5621         }
5622         
5623         //this.trigger.addClassOnOver('x-form-trigger-over');
5624         //this.trigger.addClassOnClick('x-form-trigger-click');
5625         
5626         //if(!this.width){
5627         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5628         //}
5629     },
5630
5631     // private
5632     initTrigger : function(){
5633        
5634     },
5635
5636     // private
5637     onDestroy : function(){
5638         if(this.trigger){
5639             this.trigger.removeAllListeners();
5640           //  this.trigger.remove();
5641         }
5642         //if(this.wrap){
5643         //    this.wrap.remove();
5644         //}
5645         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5646     },
5647
5648     // private
5649     onFocus : function(){
5650         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5651         /*
5652         if(!this.mimicing){
5653             this.wrap.addClass('x-trigger-wrap-focus');
5654             this.mimicing = true;
5655             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5656             if(this.monitorTab){
5657                 this.el.on("keydown", this.checkTab, this);
5658             }
5659         }
5660         */
5661     },
5662
5663     // private
5664     checkTab : function(e){
5665         if(e.getKey() == e.TAB){
5666             this.triggerBlur();
5667         }
5668     },
5669
5670     // private
5671     onBlur : function(){
5672         // do nothing
5673     },
5674
5675     // private
5676     mimicBlur : function(e, t){
5677         /*
5678         if(!this.wrap.contains(t) && this.validateBlur()){
5679             this.triggerBlur();
5680         }
5681         */
5682     },
5683
5684     // private
5685     triggerBlur : function(){
5686         this.mimicing = false;
5687         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5688         if(this.monitorTab){
5689             this.el.un("keydown", this.checkTab, this);
5690         }
5691         //this.wrap.removeClass('x-trigger-wrap-focus');
5692         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5693     },
5694
5695     // private
5696     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5697     validateBlur : function(e, t){
5698         return true;
5699     },
5700
5701     // private
5702     onDisable : function(){
5703         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5704         //if(this.wrap){
5705         //    this.wrap.addClass('x-item-disabled');
5706         //}
5707     },
5708
5709     // private
5710     onEnable : function(){
5711         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5712         //if(this.wrap){
5713         //    this.el.removeClass('x-item-disabled');
5714         //}
5715     },
5716
5717     // private
5718     onShow : function(){
5719         var ae = this.getActionEl();
5720         
5721         if(ae){
5722             ae.dom.style.display = '';
5723             ae.dom.style.visibility = 'visible';
5724         }
5725     },
5726
5727     // private
5728     
5729     onHide : function(){
5730         var ae = this.getActionEl();
5731         ae.dom.style.display = 'none';
5732     },
5733
5734     /**
5735      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5736      * by an implementing function.
5737      * @method
5738      * @param {EventObject} e
5739      */
5740     onTriggerClick : Roo.emptyFn
5741 });
5742  /*
5743  * Based on:
5744  * Ext JS Library 1.1.1
5745  * Copyright(c) 2006-2007, Ext JS, LLC.
5746  *
5747  * Originally Released Under LGPL - original licence link has changed is not relivant.
5748  *
5749  * Fork - LGPL
5750  * <script type="text/javascript">
5751  */
5752
5753
5754 /**
5755  * @class Roo.data.SortTypes
5756  * @singleton
5757  * Defines the default sorting (casting?) comparison functions used when sorting data.
5758  */
5759 Roo.data.SortTypes = {
5760     /**
5761      * Default sort that does nothing
5762      * @param {Mixed} s The value being converted
5763      * @return {Mixed} The comparison value
5764      */
5765     none : function(s){
5766         return s;
5767     },
5768     
5769     /**
5770      * The regular expression used to strip tags
5771      * @type {RegExp}
5772      * @property
5773      */
5774     stripTagsRE : /<\/?[^>]+>/gi,
5775     
5776     /**
5777      * Strips all HTML tags to sort on text only
5778      * @param {Mixed} s The value being converted
5779      * @return {String} The comparison value
5780      */
5781     asText : function(s){
5782         return String(s).replace(this.stripTagsRE, "");
5783     },
5784     
5785     /**
5786      * Strips all HTML tags to sort on text only - Case insensitive
5787      * @param {Mixed} s The value being converted
5788      * @return {String} The comparison value
5789      */
5790     asUCText : function(s){
5791         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5792     },
5793     
5794     /**
5795      * Case insensitive string
5796      * @param {Mixed} s The value being converted
5797      * @return {String} The comparison value
5798      */
5799     asUCString : function(s) {
5800         return String(s).toUpperCase();
5801     },
5802     
5803     /**
5804      * Date sorting
5805      * @param {Mixed} s The value being converted
5806      * @return {Number} The comparison value
5807      */
5808     asDate : function(s) {
5809         if(!s){
5810             return 0;
5811         }
5812         if(s instanceof Date){
5813             return s.getTime();
5814         }
5815         return Date.parse(String(s));
5816     },
5817     
5818     /**
5819      * Float sorting
5820      * @param {Mixed} s The value being converted
5821      * @return {Float} The comparison value
5822      */
5823     asFloat : function(s) {
5824         var val = parseFloat(String(s).replace(/,/g, ""));
5825         if(isNaN(val)) val = 0;
5826         return val;
5827     },
5828     
5829     /**
5830      * Integer sorting
5831      * @param {Mixed} s The value being converted
5832      * @return {Number} The comparison value
5833      */
5834     asInt : function(s) {
5835         var val = parseInt(String(s).replace(/,/g, ""));
5836         if(isNaN(val)) val = 0;
5837         return val;
5838     }
5839 };/*
5840  * Based on:
5841  * Ext JS Library 1.1.1
5842  * Copyright(c) 2006-2007, Ext JS, LLC.
5843  *
5844  * Originally Released Under LGPL - original licence link has changed is not relivant.
5845  *
5846  * Fork - LGPL
5847  * <script type="text/javascript">
5848  */
5849
5850 /**
5851 * @class Roo.data.Record
5852  * Instances of this class encapsulate both record <em>definition</em> information, and record
5853  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5854  * to access Records cached in an {@link Roo.data.Store} object.<br>
5855  * <p>
5856  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5857  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5858  * objects.<br>
5859  * <p>
5860  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5861  * @constructor
5862  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5863  * {@link #create}. The parameters are the same.
5864  * @param {Array} data An associative Array of data values keyed by the field name.
5865  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5866  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5867  * not specified an integer id is generated.
5868  */
5869 Roo.data.Record = function(data, id){
5870     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5871     this.data = data;
5872 };
5873
5874 /**
5875  * Generate a constructor for a specific record layout.
5876  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5877  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5878  * Each field definition object may contain the following properties: <ul>
5879  * <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,
5880  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5881  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5882  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5883  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5884  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5885  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5886  * this may be omitted.</p></li>
5887  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5888  * <ul><li>auto (Default, implies no conversion)</li>
5889  * <li>string</li>
5890  * <li>int</li>
5891  * <li>float</li>
5892  * <li>boolean</li>
5893  * <li>date</li></ul></p></li>
5894  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5895  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5896  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5897  * by the Reader into an object that will be stored in the Record. It is passed the
5898  * following parameters:<ul>
5899  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5900  * </ul></p></li>
5901  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5902  * </ul>
5903  * <br>usage:<br><pre><code>
5904 var TopicRecord = Roo.data.Record.create(
5905     {name: 'title', mapping: 'topic_title'},
5906     {name: 'author', mapping: 'username'},
5907     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5908     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5909     {name: 'lastPoster', mapping: 'user2'},
5910     {name: 'excerpt', mapping: 'post_text'}
5911 );
5912
5913 var myNewRecord = new TopicRecord({
5914     title: 'Do my job please',
5915     author: 'noobie',
5916     totalPosts: 1,
5917     lastPost: new Date(),
5918     lastPoster: 'Animal',
5919     excerpt: 'No way dude!'
5920 });
5921 myStore.add(myNewRecord);
5922 </code></pre>
5923  * @method create
5924  * @static
5925  */
5926 Roo.data.Record.create = function(o){
5927     var f = function(){
5928         f.superclass.constructor.apply(this, arguments);
5929     };
5930     Roo.extend(f, Roo.data.Record);
5931     var p = f.prototype;
5932     p.fields = new Roo.util.MixedCollection(false, function(field){
5933         return field.name;
5934     });
5935     for(var i = 0, len = o.length; i < len; i++){
5936         p.fields.add(new Roo.data.Field(o[i]));
5937     }
5938     f.getField = function(name){
5939         return p.fields.get(name);  
5940     };
5941     return f;
5942 };
5943
5944 Roo.data.Record.AUTO_ID = 1000;
5945 Roo.data.Record.EDIT = 'edit';
5946 Roo.data.Record.REJECT = 'reject';
5947 Roo.data.Record.COMMIT = 'commit';
5948
5949 Roo.data.Record.prototype = {
5950     /**
5951      * Readonly flag - true if this record has been modified.
5952      * @type Boolean
5953      */
5954     dirty : false,
5955     editing : false,
5956     error: null,
5957     modified: null,
5958
5959     // private
5960     join : function(store){
5961         this.store = store;
5962     },
5963
5964     /**
5965      * Set the named field to the specified value.
5966      * @param {String} name The name of the field to set.
5967      * @param {Object} value The value to set the field to.
5968      */
5969     set : function(name, value){
5970         if(this.data[name] == value){
5971             return;
5972         }
5973         this.dirty = true;
5974         if(!this.modified){
5975             this.modified = {};
5976         }
5977         if(typeof this.modified[name] == 'undefined'){
5978             this.modified[name] = this.data[name];
5979         }
5980         this.data[name] = value;
5981         if(!this.editing && this.store){
5982             this.store.afterEdit(this);
5983         }       
5984     },
5985
5986     /**
5987      * Get the value of the named field.
5988      * @param {String} name The name of the field to get the value of.
5989      * @return {Object} The value of the field.
5990      */
5991     get : function(name){
5992         return this.data[name]; 
5993     },
5994
5995     // private
5996     beginEdit : function(){
5997         this.editing = true;
5998         this.modified = {}; 
5999     },
6000
6001     // private
6002     cancelEdit : function(){
6003         this.editing = false;
6004         delete this.modified;
6005     },
6006
6007     // private
6008     endEdit : function(){
6009         this.editing = false;
6010         if(this.dirty && this.store){
6011             this.store.afterEdit(this);
6012         }
6013     },
6014
6015     /**
6016      * Usually called by the {@link Roo.data.Store} which owns the Record.
6017      * Rejects all changes made to the Record since either creation, or the last commit operation.
6018      * Modified fields are reverted to their original values.
6019      * <p>
6020      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6021      * of reject operations.
6022      */
6023     reject : function(){
6024         var m = this.modified;
6025         for(var n in m){
6026             if(typeof m[n] != "function"){
6027                 this.data[n] = m[n];
6028             }
6029         }
6030         this.dirty = false;
6031         delete this.modified;
6032         this.editing = false;
6033         if(this.store){
6034             this.store.afterReject(this);
6035         }
6036     },
6037
6038     /**
6039      * Usually called by the {@link Roo.data.Store} which owns the Record.
6040      * Commits all changes made to the Record since either creation, or the last commit operation.
6041      * <p>
6042      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6043      * of commit operations.
6044      */
6045     commit : function(){
6046         this.dirty = false;
6047         delete this.modified;
6048         this.editing = false;
6049         if(this.store){
6050             this.store.afterCommit(this);
6051         }
6052     },
6053
6054     // private
6055     hasError : function(){
6056         return this.error != null;
6057     },
6058
6059     // private
6060     clearError : function(){
6061         this.error = null;
6062     },
6063
6064     /**
6065      * Creates a copy of this record.
6066      * @param {String} id (optional) A new record id if you don't want to use this record's id
6067      * @return {Record}
6068      */
6069     copy : function(newId) {
6070         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6071     }
6072 };/*
6073  * Based on:
6074  * Ext JS Library 1.1.1
6075  * Copyright(c) 2006-2007, Ext JS, LLC.
6076  *
6077  * Originally Released Under LGPL - original licence link has changed is not relivant.
6078  *
6079  * Fork - LGPL
6080  * <script type="text/javascript">
6081  */
6082
6083
6084
6085 /**
6086  * @class Roo.data.Store
6087  * @extends Roo.util.Observable
6088  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6089  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6090  * <p>
6091  * 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
6092  * has no knowledge of the format of the data returned by the Proxy.<br>
6093  * <p>
6094  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6095  * instances from the data object. These records are cached and made available through accessor functions.
6096  * @constructor
6097  * Creates a new Store.
6098  * @param {Object} config A config object containing the objects needed for the Store to access data,
6099  * and read the data into Records.
6100  */
6101 Roo.data.Store = function(config){
6102     this.data = new Roo.util.MixedCollection(false);
6103     this.data.getKey = function(o){
6104         return o.id;
6105     };
6106     this.baseParams = {};
6107     // private
6108     this.paramNames = {
6109         "start" : "start",
6110         "limit" : "limit",
6111         "sort" : "sort",
6112         "dir" : "dir",
6113         "multisort" : "_multisort"
6114     };
6115
6116     if(config && config.data){
6117         this.inlineData = config.data;
6118         delete config.data;
6119     }
6120
6121     Roo.apply(this, config);
6122     
6123     if(this.reader){ // reader passed
6124         this.reader = Roo.factory(this.reader, Roo.data);
6125         this.reader.xmodule = this.xmodule || false;
6126         if(!this.recordType){
6127             this.recordType = this.reader.recordType;
6128         }
6129         if(this.reader.onMetaChange){
6130             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6131         }
6132     }
6133
6134     if(this.recordType){
6135         this.fields = this.recordType.prototype.fields;
6136     }
6137     this.modified = [];
6138
6139     this.addEvents({
6140         /**
6141          * @event datachanged
6142          * Fires when the data cache has changed, and a widget which is using this Store
6143          * as a Record cache should refresh its view.
6144          * @param {Store} this
6145          */
6146         datachanged : true,
6147         /**
6148          * @event metachange
6149          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6150          * @param {Store} this
6151          * @param {Object} meta The JSON metadata
6152          */
6153         metachange : true,
6154         /**
6155          * @event add
6156          * Fires when Records have been added to the Store
6157          * @param {Store} this
6158          * @param {Roo.data.Record[]} records The array of Records added
6159          * @param {Number} index The index at which the record(s) were added
6160          */
6161         add : true,
6162         /**
6163          * @event remove
6164          * Fires when a Record has been removed from the Store
6165          * @param {Store} this
6166          * @param {Roo.data.Record} record The Record that was removed
6167          * @param {Number} index The index at which the record was removed
6168          */
6169         remove : true,
6170         /**
6171          * @event update
6172          * Fires when a Record has been updated
6173          * @param {Store} this
6174          * @param {Roo.data.Record} record The Record that was updated
6175          * @param {String} operation The update operation being performed.  Value may be one of:
6176          * <pre><code>
6177  Roo.data.Record.EDIT
6178  Roo.data.Record.REJECT
6179  Roo.data.Record.COMMIT
6180          * </code></pre>
6181          */
6182         update : true,
6183         /**
6184          * @event clear
6185          * Fires when the data cache has been cleared.
6186          * @param {Store} this
6187          */
6188         clear : true,
6189         /**
6190          * @event beforeload
6191          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6192          * the load action will be canceled.
6193          * @param {Store} this
6194          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6195          */
6196         beforeload : true,
6197         /**
6198          * @event beforeloadadd
6199          * Fires after a new set of Records has been loaded.
6200          * @param {Store} this
6201          * @param {Roo.data.Record[]} records The Records that were loaded
6202          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6203          */
6204         beforeloadadd : true,
6205         /**
6206          * @event load
6207          * Fires after a new set of Records has been loaded, before they are added to the store.
6208          * @param {Store} this
6209          * @param {Roo.data.Record[]} records The Records that were loaded
6210          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6211          * @params {Object} return from reader
6212          */
6213         load : true,
6214         /**
6215          * @event loadexception
6216          * Fires if an exception occurs in the Proxy during loading.
6217          * Called with the signature of the Proxy's "loadexception" event.
6218          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6219          * 
6220          * @param {Proxy} 
6221          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6222          * @param {Object} load options 
6223          * @param {Object} jsonData from your request (normally this contains the Exception)
6224          */
6225         loadexception : true
6226     });
6227     
6228     if(this.proxy){
6229         this.proxy = Roo.factory(this.proxy, Roo.data);
6230         this.proxy.xmodule = this.xmodule || false;
6231         this.relayEvents(this.proxy,  ["loadexception"]);
6232     }
6233     this.sortToggle = {};
6234     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6235
6236     Roo.data.Store.superclass.constructor.call(this);
6237
6238     if(this.inlineData){
6239         this.loadData(this.inlineData);
6240         delete this.inlineData;
6241     }
6242 };
6243
6244 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6245      /**
6246     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6247     * without a remote query - used by combo/forms at present.
6248     */
6249     
6250     /**
6251     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6252     */
6253     /**
6254     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6255     */
6256     /**
6257     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6258     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6259     */
6260     /**
6261     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6262     * on any HTTP request
6263     */
6264     /**
6265     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6266     */
6267     /**
6268     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6269     */
6270     multiSort: false,
6271     /**
6272     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6273     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6274     */
6275     remoteSort : false,
6276
6277     /**
6278     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6279      * loaded or when a record is removed. (defaults to false).
6280     */
6281     pruneModifiedRecords : false,
6282
6283     // private
6284     lastOptions : null,
6285
6286     /**
6287      * Add Records to the Store and fires the add event.
6288      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6289      */
6290     add : function(records){
6291         records = [].concat(records);
6292         for(var i = 0, len = records.length; i < len; i++){
6293             records[i].join(this);
6294         }
6295         var index = this.data.length;
6296         this.data.addAll(records);
6297         this.fireEvent("add", this, records, index);
6298     },
6299
6300     /**
6301      * Remove a Record from the Store and fires the remove event.
6302      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6303      */
6304     remove : function(record){
6305         var index = this.data.indexOf(record);
6306         this.data.removeAt(index);
6307         if(this.pruneModifiedRecords){
6308             this.modified.remove(record);
6309         }
6310         this.fireEvent("remove", this, record, index);
6311     },
6312
6313     /**
6314      * Remove all Records from the Store and fires the clear event.
6315      */
6316     removeAll : function(){
6317         this.data.clear();
6318         if(this.pruneModifiedRecords){
6319             this.modified = [];
6320         }
6321         this.fireEvent("clear", this);
6322     },
6323
6324     /**
6325      * Inserts Records to the Store at the given index and fires the add event.
6326      * @param {Number} index The start index at which to insert the passed Records.
6327      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6328      */
6329     insert : function(index, records){
6330         records = [].concat(records);
6331         for(var i = 0, len = records.length; i < len; i++){
6332             this.data.insert(index, records[i]);
6333             records[i].join(this);
6334         }
6335         this.fireEvent("add", this, records, index);
6336     },
6337
6338     /**
6339      * Get the index within the cache of the passed Record.
6340      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6341      * @return {Number} The index of the passed Record. Returns -1 if not found.
6342      */
6343     indexOf : function(record){
6344         return this.data.indexOf(record);
6345     },
6346
6347     /**
6348      * Get the index within the cache of the Record with the passed id.
6349      * @param {String} id The id of the Record to find.
6350      * @return {Number} The index of the Record. Returns -1 if not found.
6351      */
6352     indexOfId : function(id){
6353         return this.data.indexOfKey(id);
6354     },
6355
6356     /**
6357      * Get the Record with the specified id.
6358      * @param {String} id The id of the Record to find.
6359      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6360      */
6361     getById : function(id){
6362         return this.data.key(id);
6363     },
6364
6365     /**
6366      * Get the Record at the specified index.
6367      * @param {Number} index The index of the Record to find.
6368      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6369      */
6370     getAt : function(index){
6371         return this.data.itemAt(index);
6372     },
6373
6374     /**
6375      * Returns a range of Records between specified indices.
6376      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6377      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6378      * @return {Roo.data.Record[]} An array of Records
6379      */
6380     getRange : function(start, end){
6381         return this.data.getRange(start, end);
6382     },
6383
6384     // private
6385     storeOptions : function(o){
6386         o = Roo.apply({}, o);
6387         delete o.callback;
6388         delete o.scope;
6389         this.lastOptions = o;
6390     },
6391
6392     /**
6393      * Loads the Record cache from the configured Proxy using the configured Reader.
6394      * <p>
6395      * If using remote paging, then the first load call must specify the <em>start</em>
6396      * and <em>limit</em> properties in the options.params property to establish the initial
6397      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6398      * <p>
6399      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6400      * and this call will return before the new data has been loaded. Perform any post-processing
6401      * in a callback function, or in a "load" event handler.</strong>
6402      * <p>
6403      * @param {Object} options An object containing properties which control loading options:<ul>
6404      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6405      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6406      * passed the following arguments:<ul>
6407      * <li>r : Roo.data.Record[]</li>
6408      * <li>options: Options object from the load call</li>
6409      * <li>success: Boolean success indicator</li></ul></li>
6410      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6411      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6412      * </ul>
6413      */
6414     load : function(options){
6415         options = options || {};
6416         if(this.fireEvent("beforeload", this, options) !== false){
6417             this.storeOptions(options);
6418             var p = Roo.apply(options.params || {}, this.baseParams);
6419             // if meta was not loaded from remote source.. try requesting it.
6420             if (!this.reader.metaFromRemote) {
6421                 p._requestMeta = 1;
6422             }
6423             if(this.sortInfo && this.remoteSort){
6424                 var pn = this.paramNames;
6425                 p[pn["sort"]] = this.sortInfo.field;
6426                 p[pn["dir"]] = this.sortInfo.direction;
6427             }
6428             if (this.multiSort) {
6429                 var pn = this.paramNames;
6430                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6431             }
6432             
6433             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6434         }
6435     },
6436
6437     /**
6438      * Reloads the Record cache from the configured Proxy using the configured Reader and
6439      * the options from the last load operation performed.
6440      * @param {Object} options (optional) An object containing properties which may override the options
6441      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6442      * the most recently used options are reused).
6443      */
6444     reload : function(options){
6445         this.load(Roo.applyIf(options||{}, this.lastOptions));
6446     },
6447
6448     // private
6449     // Called as a callback by the Reader during a load operation.
6450     loadRecords : function(o, options, success){
6451         if(!o || success === false){
6452             if(success !== false){
6453                 this.fireEvent("load", this, [], options, o);
6454             }
6455             if(options.callback){
6456                 options.callback.call(options.scope || this, [], options, false);
6457             }
6458             return;
6459         }
6460         // if data returned failure - throw an exception.
6461         if (o.success === false) {
6462             // show a message if no listener is registered.
6463             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6464                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6465             }
6466             // loadmask wil be hooked into this..
6467             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6468             return;
6469         }
6470         var r = o.records, t = o.totalRecords || r.length;
6471         
6472         this.fireEvent("beforeloadadd", this, r, options, o);
6473         
6474         if(!options || options.add !== true){
6475             if(this.pruneModifiedRecords){
6476                 this.modified = [];
6477             }
6478             for(var i = 0, len = r.length; i < len; i++){
6479                 r[i].join(this);
6480             }
6481             if(this.snapshot){
6482                 this.data = this.snapshot;
6483                 delete this.snapshot;
6484             }
6485             this.data.clear();
6486             this.data.addAll(r);
6487             this.totalLength = t;
6488             this.applySort();
6489             this.fireEvent("datachanged", this);
6490         }else{
6491             this.totalLength = Math.max(t, this.data.length+r.length);
6492             this.add(r);
6493         }
6494         this.fireEvent("load", this, r, options, o);
6495         if(options.callback){
6496             options.callback.call(options.scope || this, r, options, true);
6497         }
6498     },
6499
6500
6501     /**
6502      * Loads data from a passed data block. A Reader which understands the format of the data
6503      * must have been configured in the constructor.
6504      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6505      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6506      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6507      */
6508     loadData : function(o, append){
6509         var r = this.reader.readRecords(o);
6510         this.loadRecords(r, {add: append}, true);
6511     },
6512
6513     /**
6514      * Gets the number of cached records.
6515      * <p>
6516      * <em>If using paging, this may not be the total size of the dataset. If the data object
6517      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6518      * the data set size</em>
6519      */
6520     getCount : function(){
6521         return this.data.length || 0;
6522     },
6523
6524     /**
6525      * Gets the total number of records in the dataset as returned by the server.
6526      * <p>
6527      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6528      * the dataset size</em>
6529      */
6530     getTotalCount : function(){
6531         return this.totalLength || 0;
6532     },
6533
6534     /**
6535      * Returns the sort state of the Store as an object with two properties:
6536      * <pre><code>
6537  field {String} The name of the field by which the Records are sorted
6538  direction {String} The sort order, "ASC" or "DESC"
6539      * </code></pre>
6540      */
6541     getSortState : function(){
6542         return this.sortInfo;
6543     },
6544
6545     // private
6546     applySort : function(){
6547         if(this.sortInfo && !this.remoteSort){
6548             var s = this.sortInfo, f = s.field;
6549             var st = this.fields.get(f).sortType;
6550             var fn = function(r1, r2){
6551                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6552                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6553             };
6554             this.data.sort(s.direction, fn);
6555             if(this.snapshot && this.snapshot != this.data){
6556                 this.snapshot.sort(s.direction, fn);
6557             }
6558         }
6559     },
6560
6561     /**
6562      * Sets the default sort column and order to be used by the next load operation.
6563      * @param {String} fieldName The name of the field to sort by.
6564      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6565      */
6566     setDefaultSort : function(field, dir){
6567         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6568     },
6569
6570     /**
6571      * Sort the Records.
6572      * If remote sorting is used, the sort is performed on the server, and the cache is
6573      * reloaded. If local sorting is used, the cache is sorted internally.
6574      * @param {String} fieldName The name of the field to sort by.
6575      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6576      */
6577     sort : function(fieldName, dir){
6578         var f = this.fields.get(fieldName);
6579         if(!dir){
6580             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6581             
6582             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6583                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6584             }else{
6585                 dir = f.sortDir;
6586             }
6587         }
6588         this.sortToggle[f.name] = dir;
6589         this.sortInfo = {field: f.name, direction: dir};
6590         if(!this.remoteSort){
6591             this.applySort();
6592             this.fireEvent("datachanged", this);
6593         }else{
6594             this.load(this.lastOptions);
6595         }
6596     },
6597
6598     /**
6599      * Calls the specified function for each of the Records in the cache.
6600      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6601      * Returning <em>false</em> aborts and exits the iteration.
6602      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6603      */
6604     each : function(fn, scope){
6605         this.data.each(fn, scope);
6606     },
6607
6608     /**
6609      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6610      * (e.g., during paging).
6611      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6612      */
6613     getModifiedRecords : function(){
6614         return this.modified;
6615     },
6616
6617     // private
6618     createFilterFn : function(property, value, anyMatch){
6619         if(!value.exec){ // not a regex
6620             value = String(value);
6621             if(value.length == 0){
6622                 return false;
6623             }
6624             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6625         }
6626         return function(r){
6627             return value.test(r.data[property]);
6628         };
6629     },
6630
6631     /**
6632      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6633      * @param {String} property A field on your records
6634      * @param {Number} start The record index to start at (defaults to 0)
6635      * @param {Number} end The last record index to include (defaults to length - 1)
6636      * @return {Number} The sum
6637      */
6638     sum : function(property, start, end){
6639         var rs = this.data.items, v = 0;
6640         start = start || 0;
6641         end = (end || end === 0) ? end : rs.length-1;
6642
6643         for(var i = start; i <= end; i++){
6644             v += (rs[i].data[property] || 0);
6645         }
6646         return v;
6647     },
6648
6649     /**
6650      * Filter the records by a specified property.
6651      * @param {String} field A field on your records
6652      * @param {String/RegExp} value Either a string that the field
6653      * should start with or a RegExp to test against the field
6654      * @param {Boolean} anyMatch True to match any part not just the beginning
6655      */
6656     filter : function(property, value, anyMatch){
6657         var fn = this.createFilterFn(property, value, anyMatch);
6658         return fn ? this.filterBy(fn) : this.clearFilter();
6659     },
6660
6661     /**
6662      * Filter by a function. The specified function will be called with each
6663      * record in this data source. If the function returns true the record is included,
6664      * otherwise it is filtered.
6665      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6666      * @param {Object} scope (optional) The scope of the function (defaults to this)
6667      */
6668     filterBy : function(fn, scope){
6669         this.snapshot = this.snapshot || this.data;
6670         this.data = this.queryBy(fn, scope||this);
6671         this.fireEvent("datachanged", this);
6672     },
6673
6674     /**
6675      * Query the records by a specified property.
6676      * @param {String} field A field on your records
6677      * @param {String/RegExp} value Either a string that the field
6678      * should start with or a RegExp to test against the field
6679      * @param {Boolean} anyMatch True to match any part not just the beginning
6680      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6681      */
6682     query : function(property, value, anyMatch){
6683         var fn = this.createFilterFn(property, value, anyMatch);
6684         return fn ? this.queryBy(fn) : this.data.clone();
6685     },
6686
6687     /**
6688      * Query by a function. The specified function will be called with each
6689      * record in this data source. If the function returns true the record is included
6690      * in the results.
6691      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6692      * @param {Object} scope (optional) The scope of the function (defaults to this)
6693       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6694      **/
6695     queryBy : function(fn, scope){
6696         var data = this.snapshot || this.data;
6697         return data.filterBy(fn, scope||this);
6698     },
6699
6700     /**
6701      * Collects unique values for a particular dataIndex from this store.
6702      * @param {String} dataIndex The property to collect
6703      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6704      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6705      * @return {Array} An array of the unique values
6706      **/
6707     collect : function(dataIndex, allowNull, bypassFilter){
6708         var d = (bypassFilter === true && this.snapshot) ?
6709                 this.snapshot.items : this.data.items;
6710         var v, sv, r = [], l = {};
6711         for(var i = 0, len = d.length; i < len; i++){
6712             v = d[i].data[dataIndex];
6713             sv = String(v);
6714             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6715                 l[sv] = true;
6716                 r[r.length] = v;
6717             }
6718         }
6719         return r;
6720     },
6721
6722     /**
6723      * Revert to a view of the Record cache with no filtering applied.
6724      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6725      */
6726     clearFilter : function(suppressEvent){
6727         if(this.snapshot && this.snapshot != this.data){
6728             this.data = this.snapshot;
6729             delete this.snapshot;
6730             if(suppressEvent !== true){
6731                 this.fireEvent("datachanged", this);
6732             }
6733         }
6734     },
6735
6736     // private
6737     afterEdit : function(record){
6738         if(this.modified.indexOf(record) == -1){
6739             this.modified.push(record);
6740         }
6741         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6742     },
6743     
6744     // private
6745     afterReject : function(record){
6746         this.modified.remove(record);
6747         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6748     },
6749
6750     // private
6751     afterCommit : function(record){
6752         this.modified.remove(record);
6753         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6754     },
6755
6756     /**
6757      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6758      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6759      */
6760     commitChanges : function(){
6761         var m = this.modified.slice(0);
6762         this.modified = [];
6763         for(var i = 0, len = m.length; i < len; i++){
6764             m[i].commit();
6765         }
6766     },
6767
6768     /**
6769      * Cancel outstanding changes on all changed records.
6770      */
6771     rejectChanges : function(){
6772         var m = this.modified.slice(0);
6773         this.modified = [];
6774         for(var i = 0, len = m.length; i < len; i++){
6775             m[i].reject();
6776         }
6777     },
6778
6779     onMetaChange : function(meta, rtype, o){
6780         this.recordType = rtype;
6781         this.fields = rtype.prototype.fields;
6782         delete this.snapshot;
6783         this.sortInfo = meta.sortInfo || this.sortInfo;
6784         this.modified = [];
6785         this.fireEvent('metachange', this, this.reader.meta);
6786     },
6787     
6788     moveIndex : function(data, type)
6789     {
6790         var index = this.indexOf(data);
6791         
6792         var newIndex = index + type;
6793         
6794         this.remove(data);
6795         
6796         this.insert(newIndex, data);
6797         
6798     }
6799 });/*
6800  * Based on:
6801  * Ext JS Library 1.1.1
6802  * Copyright(c) 2006-2007, Ext JS, LLC.
6803  *
6804  * Originally Released Under LGPL - original licence link has changed is not relivant.
6805  *
6806  * Fork - LGPL
6807  * <script type="text/javascript">
6808  */
6809
6810 /**
6811  * @class Roo.data.SimpleStore
6812  * @extends Roo.data.Store
6813  * Small helper class to make creating Stores from Array data easier.
6814  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6815  * @cfg {Array} fields An array of field definition objects, or field name strings.
6816  * @cfg {Array} data The multi-dimensional array of data
6817  * @constructor
6818  * @param {Object} config
6819  */
6820 Roo.data.SimpleStore = function(config){
6821     Roo.data.SimpleStore.superclass.constructor.call(this, {
6822         isLocal : true,
6823         reader: new Roo.data.ArrayReader({
6824                 id: config.id
6825             },
6826             Roo.data.Record.create(config.fields)
6827         ),
6828         proxy : new Roo.data.MemoryProxy(config.data)
6829     });
6830     this.load();
6831 };
6832 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6833  * Based on:
6834  * Ext JS Library 1.1.1
6835  * Copyright(c) 2006-2007, Ext JS, LLC.
6836  *
6837  * Originally Released Under LGPL - original licence link has changed is not relivant.
6838  *
6839  * Fork - LGPL
6840  * <script type="text/javascript">
6841  */
6842
6843 /**
6844 /**
6845  * @extends Roo.data.Store
6846  * @class Roo.data.JsonStore
6847  * Small helper class to make creating Stores for JSON data easier. <br/>
6848 <pre><code>
6849 var store = new Roo.data.JsonStore({
6850     url: 'get-images.php',
6851     root: 'images',
6852     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6853 });
6854 </code></pre>
6855  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6856  * JsonReader and HttpProxy (unless inline data is provided).</b>
6857  * @cfg {Array} fields An array of field definition objects, or field name strings.
6858  * @constructor
6859  * @param {Object} config
6860  */
6861 Roo.data.JsonStore = function(c){
6862     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6863         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6864         reader: new Roo.data.JsonReader(c, c.fields)
6865     }));
6866 };
6867 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6868  * Based on:
6869  * Ext JS Library 1.1.1
6870  * Copyright(c) 2006-2007, Ext JS, LLC.
6871  *
6872  * Originally Released Under LGPL - original licence link has changed is not relivant.
6873  *
6874  * Fork - LGPL
6875  * <script type="text/javascript">
6876  */
6877
6878  
6879 Roo.data.Field = function(config){
6880     if(typeof config == "string"){
6881         config = {name: config};
6882     }
6883     Roo.apply(this, config);
6884     
6885     if(!this.type){
6886         this.type = "auto";
6887     }
6888     
6889     var st = Roo.data.SortTypes;
6890     // named sortTypes are supported, here we look them up
6891     if(typeof this.sortType == "string"){
6892         this.sortType = st[this.sortType];
6893     }
6894     
6895     // set default sortType for strings and dates
6896     if(!this.sortType){
6897         switch(this.type){
6898             case "string":
6899                 this.sortType = st.asUCString;
6900                 break;
6901             case "date":
6902                 this.sortType = st.asDate;
6903                 break;
6904             default:
6905                 this.sortType = st.none;
6906         }
6907     }
6908
6909     // define once
6910     var stripRe = /[\$,%]/g;
6911
6912     // prebuilt conversion function for this field, instead of
6913     // switching every time we're reading a value
6914     if(!this.convert){
6915         var cv, dateFormat = this.dateFormat;
6916         switch(this.type){
6917             case "":
6918             case "auto":
6919             case undefined:
6920                 cv = function(v){ return v; };
6921                 break;
6922             case "string":
6923                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6924                 break;
6925             case "int":
6926                 cv = function(v){
6927                     return v !== undefined && v !== null && v !== '' ?
6928                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6929                     };
6930                 break;
6931             case "float":
6932                 cv = function(v){
6933                     return v !== undefined && v !== null && v !== '' ?
6934                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6935                     };
6936                 break;
6937             case "bool":
6938             case "boolean":
6939                 cv = function(v){ return v === true || v === "true" || v == 1; };
6940                 break;
6941             case "date":
6942                 cv = function(v){
6943                     if(!v){
6944                         return '';
6945                     }
6946                     if(v instanceof Date){
6947                         return v;
6948                     }
6949                     if(dateFormat){
6950                         if(dateFormat == "timestamp"){
6951                             return new Date(v*1000);
6952                         }
6953                         return Date.parseDate(v, dateFormat);
6954                     }
6955                     var parsed = Date.parse(v);
6956                     return parsed ? new Date(parsed) : null;
6957                 };
6958              break;
6959             
6960         }
6961         this.convert = cv;
6962     }
6963 };
6964
6965 Roo.data.Field.prototype = {
6966     dateFormat: null,
6967     defaultValue: "",
6968     mapping: null,
6969     sortType : null,
6970     sortDir : "ASC"
6971 };/*
6972  * Based on:
6973  * Ext JS Library 1.1.1
6974  * Copyright(c) 2006-2007, Ext JS, LLC.
6975  *
6976  * Originally Released Under LGPL - original licence link has changed is not relivant.
6977  *
6978  * Fork - LGPL
6979  * <script type="text/javascript">
6980  */
6981  
6982 // Base class for reading structured data from a data source.  This class is intended to be
6983 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6984
6985 /**
6986  * @class Roo.data.DataReader
6987  * Base class for reading structured data from a data source.  This class is intended to be
6988  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6989  */
6990
6991 Roo.data.DataReader = function(meta, recordType){
6992     
6993     this.meta = meta;
6994     
6995     this.recordType = recordType instanceof Array ? 
6996         Roo.data.Record.create(recordType) : recordType;
6997 };
6998
6999 Roo.data.DataReader.prototype = {
7000      /**
7001      * Create an empty record
7002      * @param {Object} data (optional) - overlay some values
7003      * @return {Roo.data.Record} record created.
7004      */
7005     newRow :  function(d) {
7006         var da =  {};
7007         this.recordType.prototype.fields.each(function(c) {
7008             switch( c.type) {
7009                 case 'int' : da[c.name] = 0; break;
7010                 case 'date' : da[c.name] = new Date(); break;
7011                 case 'float' : da[c.name] = 0.0; break;
7012                 case 'boolean' : da[c.name] = false; break;
7013                 default : da[c.name] = ""; break;
7014             }
7015             
7016         });
7017         return new this.recordType(Roo.apply(da, d));
7018     }
7019     
7020 };/*
7021  * Based on:
7022  * Ext JS Library 1.1.1
7023  * Copyright(c) 2006-2007, Ext JS, LLC.
7024  *
7025  * Originally Released Under LGPL - original licence link has changed is not relivant.
7026  *
7027  * Fork - LGPL
7028  * <script type="text/javascript">
7029  */
7030
7031 /**
7032  * @class Roo.data.DataProxy
7033  * @extends Roo.data.Observable
7034  * This class is an abstract base class for implementations which provide retrieval of
7035  * unformatted data objects.<br>
7036  * <p>
7037  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7038  * (of the appropriate type which knows how to parse the data object) to provide a block of
7039  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7040  * <p>
7041  * Custom implementations must implement the load method as described in
7042  * {@link Roo.data.HttpProxy#load}.
7043  */
7044 Roo.data.DataProxy = function(){
7045     this.addEvents({
7046         /**
7047          * @event beforeload
7048          * Fires before a network request is made to retrieve a data object.
7049          * @param {Object} This DataProxy object.
7050          * @param {Object} params The params parameter to the load function.
7051          */
7052         beforeload : true,
7053         /**
7054          * @event load
7055          * Fires before the load method's callback is called.
7056          * @param {Object} This DataProxy object.
7057          * @param {Object} o The data object.
7058          * @param {Object} arg The callback argument object passed to the load function.
7059          */
7060         load : true,
7061         /**
7062          * @event loadexception
7063          * Fires if an Exception occurs during data retrieval.
7064          * @param {Object} This DataProxy object.
7065          * @param {Object} o The data object.
7066          * @param {Object} arg The callback argument object passed to the load function.
7067          * @param {Object} e The Exception.
7068          */
7069         loadexception : true
7070     });
7071     Roo.data.DataProxy.superclass.constructor.call(this);
7072 };
7073
7074 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7075
7076     /**
7077      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7078      */
7079 /*
7080  * Based on:
7081  * Ext JS Library 1.1.1
7082  * Copyright(c) 2006-2007, Ext JS, LLC.
7083  *
7084  * Originally Released Under LGPL - original licence link has changed is not relivant.
7085  *
7086  * Fork - LGPL
7087  * <script type="text/javascript">
7088  */
7089 /**
7090  * @class Roo.data.MemoryProxy
7091  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7092  * to the Reader when its load method is called.
7093  * @constructor
7094  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7095  */
7096 Roo.data.MemoryProxy = function(data){
7097     if (data.data) {
7098         data = data.data;
7099     }
7100     Roo.data.MemoryProxy.superclass.constructor.call(this);
7101     this.data = data;
7102 };
7103
7104 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7105     /**
7106      * Load data from the requested source (in this case an in-memory
7107      * data object passed to the constructor), read the data object into
7108      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7109      * process that block using the passed callback.
7110      * @param {Object} params This parameter is not used by the MemoryProxy class.
7111      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7112      * object into a block of Roo.data.Records.
7113      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7114      * The function must be passed <ul>
7115      * <li>The Record block object</li>
7116      * <li>The "arg" argument from the load function</li>
7117      * <li>A boolean success indicator</li>
7118      * </ul>
7119      * @param {Object} scope The scope in which to call the callback
7120      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7121      */
7122     load : function(params, reader, callback, scope, arg){
7123         params = params || {};
7124         var result;
7125         try {
7126             result = reader.readRecords(this.data);
7127         }catch(e){
7128             this.fireEvent("loadexception", this, arg, null, e);
7129             callback.call(scope, null, arg, false);
7130             return;
7131         }
7132         callback.call(scope, result, arg, true);
7133     },
7134     
7135     // private
7136     update : function(params, records){
7137         
7138     }
7139 });/*
7140  * Based on:
7141  * Ext JS Library 1.1.1
7142  * Copyright(c) 2006-2007, Ext JS, LLC.
7143  *
7144  * Originally Released Under LGPL - original licence link has changed is not relivant.
7145  *
7146  * Fork - LGPL
7147  * <script type="text/javascript">
7148  */
7149 /**
7150  * @class Roo.data.HttpProxy
7151  * @extends Roo.data.DataProxy
7152  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7153  * configured to reference a certain URL.<br><br>
7154  * <p>
7155  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7156  * from which the running page was served.<br><br>
7157  * <p>
7158  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7159  * <p>
7160  * Be aware that to enable the browser to parse an XML document, the server must set
7161  * the Content-Type header in the HTTP response to "text/xml".
7162  * @constructor
7163  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7164  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7165  * will be used to make the request.
7166  */
7167 Roo.data.HttpProxy = function(conn){
7168     Roo.data.HttpProxy.superclass.constructor.call(this);
7169     // is conn a conn config or a real conn?
7170     this.conn = conn;
7171     this.useAjax = !conn || !conn.events;
7172   
7173 };
7174
7175 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7176     // thse are take from connection...
7177     
7178     /**
7179      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7180      */
7181     /**
7182      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7183      * extra parameters to each request made by this object. (defaults to undefined)
7184      */
7185     /**
7186      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7187      *  to each request made by this object. (defaults to undefined)
7188      */
7189     /**
7190      * @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)
7191      */
7192     /**
7193      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7194      */
7195      /**
7196      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7197      * @type Boolean
7198      */
7199   
7200
7201     /**
7202      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7203      * @type Boolean
7204      */
7205     /**
7206      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7207      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7208      * a finer-grained basis than the DataProxy events.
7209      */
7210     getConnection : function(){
7211         return this.useAjax ? Roo.Ajax : this.conn;
7212     },
7213
7214     /**
7215      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7216      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7217      * process that block using the passed callback.
7218      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7219      * for the request to the remote server.
7220      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7221      * object into a block of Roo.data.Records.
7222      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7223      * The function must be passed <ul>
7224      * <li>The Record block object</li>
7225      * <li>The "arg" argument from the load function</li>
7226      * <li>A boolean success indicator</li>
7227      * </ul>
7228      * @param {Object} scope The scope in which to call the callback
7229      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7230      */
7231     load : function(params, reader, callback, scope, arg){
7232         if(this.fireEvent("beforeload", this, params) !== false){
7233             var  o = {
7234                 params : params || {},
7235                 request: {
7236                     callback : callback,
7237                     scope : scope,
7238                     arg : arg
7239                 },
7240                 reader: reader,
7241                 callback : this.loadResponse,
7242                 scope: this
7243             };
7244             if(this.useAjax){
7245                 Roo.applyIf(o, this.conn);
7246                 if(this.activeRequest){
7247                     Roo.Ajax.abort(this.activeRequest);
7248                 }
7249                 this.activeRequest = Roo.Ajax.request(o);
7250             }else{
7251                 this.conn.request(o);
7252             }
7253         }else{
7254             callback.call(scope||this, null, arg, false);
7255         }
7256     },
7257
7258     // private
7259     loadResponse : function(o, success, response){
7260         delete this.activeRequest;
7261         if(!success){
7262             this.fireEvent("loadexception", this, o, response);
7263             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7264             return;
7265         }
7266         var result;
7267         try {
7268             result = o.reader.read(response);
7269         }catch(e){
7270             this.fireEvent("loadexception", this, o, response, e);
7271             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7272             return;
7273         }
7274         
7275         this.fireEvent("load", this, o, o.request.arg);
7276         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7277     },
7278
7279     // private
7280     update : function(dataSet){
7281
7282     },
7283
7284     // private
7285     updateResponse : function(dataSet){
7286
7287     }
7288 });/*
7289  * Based on:
7290  * Ext JS Library 1.1.1
7291  * Copyright(c) 2006-2007, Ext JS, LLC.
7292  *
7293  * Originally Released Under LGPL - original licence link has changed is not relivant.
7294  *
7295  * Fork - LGPL
7296  * <script type="text/javascript">
7297  */
7298
7299 /**
7300  * @class Roo.data.ScriptTagProxy
7301  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7302  * other than the originating domain of the running page.<br><br>
7303  * <p>
7304  * <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
7305  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7306  * <p>
7307  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7308  * source code that is used as the source inside a &lt;script> tag.<br><br>
7309  * <p>
7310  * In order for the browser to process the returned data, the server must wrap the data object
7311  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7312  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7313  * depending on whether the callback name was passed:
7314  * <p>
7315  * <pre><code>
7316 boolean scriptTag = false;
7317 String cb = request.getParameter("callback");
7318 if (cb != null) {
7319     scriptTag = true;
7320     response.setContentType("text/javascript");
7321 } else {
7322     response.setContentType("application/x-json");
7323 }
7324 Writer out = response.getWriter();
7325 if (scriptTag) {
7326     out.write(cb + "(");
7327 }
7328 out.print(dataBlock.toJsonString());
7329 if (scriptTag) {
7330     out.write(");");
7331 }
7332 </pre></code>
7333  *
7334  * @constructor
7335  * @param {Object} config A configuration object.
7336  */
7337 Roo.data.ScriptTagProxy = function(config){
7338     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7339     Roo.apply(this, config);
7340     this.head = document.getElementsByTagName("head")[0];
7341 };
7342
7343 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7344
7345 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7346     /**
7347      * @cfg {String} url The URL from which to request the data object.
7348      */
7349     /**
7350      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7351      */
7352     timeout : 30000,
7353     /**
7354      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7355      * the server the name of the callback function set up by the load call to process the returned data object.
7356      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7357      * javascript output which calls this named function passing the data object as its only parameter.
7358      */
7359     callbackParam : "callback",
7360     /**
7361      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7362      * name to the request.
7363      */
7364     nocache : true,
7365
7366     /**
7367      * Load data from the configured URL, read the data object into
7368      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7369      * process that block using the passed callback.
7370      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7371      * for the request to the remote server.
7372      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7373      * object into a block of Roo.data.Records.
7374      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7375      * The function must be passed <ul>
7376      * <li>The Record block object</li>
7377      * <li>The "arg" argument from the load function</li>
7378      * <li>A boolean success indicator</li>
7379      * </ul>
7380      * @param {Object} scope The scope in which to call the callback
7381      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7382      */
7383     load : function(params, reader, callback, scope, arg){
7384         if(this.fireEvent("beforeload", this, params) !== false){
7385
7386             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7387
7388             var url = this.url;
7389             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7390             if(this.nocache){
7391                 url += "&_dc=" + (new Date().getTime());
7392             }
7393             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7394             var trans = {
7395                 id : transId,
7396                 cb : "stcCallback"+transId,
7397                 scriptId : "stcScript"+transId,
7398                 params : params,
7399                 arg : arg,
7400                 url : url,
7401                 callback : callback,
7402                 scope : scope,
7403                 reader : reader
7404             };
7405             var conn = this;
7406
7407             window[trans.cb] = function(o){
7408                 conn.handleResponse(o, trans);
7409             };
7410
7411             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7412
7413             if(this.autoAbort !== false){
7414                 this.abort();
7415             }
7416
7417             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7418
7419             var script = document.createElement("script");
7420             script.setAttribute("src", url);
7421             script.setAttribute("type", "text/javascript");
7422             script.setAttribute("id", trans.scriptId);
7423             this.head.appendChild(script);
7424
7425             this.trans = trans;
7426         }else{
7427             callback.call(scope||this, null, arg, false);
7428         }
7429     },
7430
7431     // private
7432     isLoading : function(){
7433         return this.trans ? true : false;
7434     },
7435
7436     /**
7437      * Abort the current server request.
7438      */
7439     abort : function(){
7440         if(this.isLoading()){
7441             this.destroyTrans(this.trans);
7442         }
7443     },
7444
7445     // private
7446     destroyTrans : function(trans, isLoaded){
7447         this.head.removeChild(document.getElementById(trans.scriptId));
7448         clearTimeout(trans.timeoutId);
7449         if(isLoaded){
7450             window[trans.cb] = undefined;
7451             try{
7452                 delete window[trans.cb];
7453             }catch(e){}
7454         }else{
7455             // if hasn't been loaded, wait for load to remove it to prevent script error
7456             window[trans.cb] = function(){
7457                 window[trans.cb] = undefined;
7458                 try{
7459                     delete window[trans.cb];
7460                 }catch(e){}
7461             };
7462         }
7463     },
7464
7465     // private
7466     handleResponse : function(o, trans){
7467         this.trans = false;
7468         this.destroyTrans(trans, true);
7469         var result;
7470         try {
7471             result = trans.reader.readRecords(o);
7472         }catch(e){
7473             this.fireEvent("loadexception", this, o, trans.arg, e);
7474             trans.callback.call(trans.scope||window, null, trans.arg, false);
7475             return;
7476         }
7477         this.fireEvent("load", this, o, trans.arg);
7478         trans.callback.call(trans.scope||window, result, trans.arg, true);
7479     },
7480
7481     // private
7482     handleFailure : function(trans){
7483         this.trans = false;
7484         this.destroyTrans(trans, false);
7485         this.fireEvent("loadexception", this, null, trans.arg);
7486         trans.callback.call(trans.scope||window, null, trans.arg, false);
7487     }
7488 });/*
7489  * Based on:
7490  * Ext JS Library 1.1.1
7491  * Copyright(c) 2006-2007, Ext JS, LLC.
7492  *
7493  * Originally Released Under LGPL - original licence link has changed is not relivant.
7494  *
7495  * Fork - LGPL
7496  * <script type="text/javascript">
7497  */
7498
7499 /**
7500  * @class Roo.data.JsonReader
7501  * @extends Roo.data.DataReader
7502  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7503  * based on mappings in a provided Roo.data.Record constructor.
7504  * 
7505  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7506  * in the reply previously. 
7507  * 
7508  * <p>
7509  * Example code:
7510  * <pre><code>
7511 var RecordDef = Roo.data.Record.create([
7512     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7513     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7514 ]);
7515 var myReader = new Roo.data.JsonReader({
7516     totalProperty: "results",    // The property which contains the total dataset size (optional)
7517     root: "rows",                // The property which contains an Array of row objects
7518     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7519 }, RecordDef);
7520 </code></pre>
7521  * <p>
7522  * This would consume a JSON file like this:
7523  * <pre><code>
7524 { 'results': 2, 'rows': [
7525     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7526     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7527 }
7528 </code></pre>
7529  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7530  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7531  * paged from the remote server.
7532  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7533  * @cfg {String} root name of the property which contains the Array of row objects.
7534  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7535  * @constructor
7536  * Create a new JsonReader
7537  * @param {Object} meta Metadata configuration options
7538  * @param {Object} recordType Either an Array of field definition objects,
7539  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7540  */
7541 Roo.data.JsonReader = function(meta, recordType){
7542     
7543     meta = meta || {};
7544     // set some defaults:
7545     Roo.applyIf(meta, {
7546         totalProperty: 'total',
7547         successProperty : 'success',
7548         root : 'data',
7549         id : 'id'
7550     });
7551     
7552     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7553 };
7554 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7555     
7556     /**
7557      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7558      * Used by Store query builder to append _requestMeta to params.
7559      * 
7560      */
7561     metaFromRemote : false,
7562     /**
7563      * This method is only used by a DataProxy which has retrieved data from a remote server.
7564      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7565      * @return {Object} data A data block which is used by an Roo.data.Store object as
7566      * a cache of Roo.data.Records.
7567      */
7568     read : function(response){
7569         var json = response.responseText;
7570        
7571         var o = /* eval:var:o */ eval("("+json+")");
7572         if(!o) {
7573             throw {message: "JsonReader.read: Json object not found"};
7574         }
7575         
7576         if(o.metaData){
7577             
7578             delete this.ef;
7579             this.metaFromRemote = true;
7580             this.meta = o.metaData;
7581             this.recordType = Roo.data.Record.create(o.metaData.fields);
7582             this.onMetaChange(this.meta, this.recordType, o);
7583         }
7584         return this.readRecords(o);
7585     },
7586
7587     // private function a store will implement
7588     onMetaChange : function(meta, recordType, o){
7589
7590     },
7591
7592     /**
7593          * @ignore
7594          */
7595     simpleAccess: function(obj, subsc) {
7596         return obj[subsc];
7597     },
7598
7599         /**
7600          * @ignore
7601          */
7602     getJsonAccessor: function(){
7603         var re = /[\[\.]/;
7604         return function(expr) {
7605             try {
7606                 return(re.test(expr))
7607                     ? new Function("obj", "return obj." + expr)
7608                     : function(obj){
7609                         return obj[expr];
7610                     };
7611             } catch(e){}
7612             return Roo.emptyFn;
7613         };
7614     }(),
7615
7616     /**
7617      * Create a data block containing Roo.data.Records from an XML document.
7618      * @param {Object} o An object which contains an Array of row objects in the property specified
7619      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7620      * which contains the total size of the dataset.
7621      * @return {Object} data A data block which is used by an Roo.data.Store object as
7622      * a cache of Roo.data.Records.
7623      */
7624     readRecords : function(o){
7625         /**
7626          * After any data loads, the raw JSON data is available for further custom processing.
7627          * @type Object
7628          */
7629         this.o = o;
7630         var s = this.meta, Record = this.recordType,
7631             f = Record.prototype.fields, fi = f.items, fl = f.length;
7632
7633 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7634         if (!this.ef) {
7635             if(s.totalProperty) {
7636                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7637                 }
7638                 if(s.successProperty) {
7639                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7640                 }
7641                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7642                 if (s.id) {
7643                         var g = this.getJsonAccessor(s.id);
7644                         this.getId = function(rec) {
7645                                 var r = g(rec);
7646                                 return (r === undefined || r === "") ? null : r;
7647                         };
7648                 } else {
7649                         this.getId = function(){return null;};
7650                 }
7651             this.ef = [];
7652             for(var jj = 0; jj < fl; jj++){
7653                 f = fi[jj];
7654                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7655                 this.ef[jj] = this.getJsonAccessor(map);
7656             }
7657         }
7658
7659         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7660         if(s.totalProperty){
7661             var vt = parseInt(this.getTotal(o), 10);
7662             if(!isNaN(vt)){
7663                 totalRecords = vt;
7664             }
7665         }
7666         if(s.successProperty){
7667             var vs = this.getSuccess(o);
7668             if(vs === false || vs === 'false'){
7669                 success = false;
7670             }
7671         }
7672         var records = [];
7673             for(var i = 0; i < c; i++){
7674                     var n = root[i];
7675                 var values = {};
7676                 var id = this.getId(n);
7677                 for(var j = 0; j < fl; j++){
7678                     f = fi[j];
7679                 var v = this.ef[j](n);
7680                 if (!f.convert) {
7681                     Roo.log('missing convert for ' + f.name);
7682                     Roo.log(f);
7683                     continue;
7684                 }
7685                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7686                 }
7687                 var record = new Record(values, id);
7688                 record.json = n;
7689                 records[i] = record;
7690             }
7691             return {
7692             raw : o,
7693                 success : success,
7694                 records : records,
7695                 totalRecords : totalRecords
7696             };
7697     }
7698 });/*
7699  * Based on:
7700  * Ext JS Library 1.1.1
7701  * Copyright(c) 2006-2007, Ext JS, LLC.
7702  *
7703  * Originally Released Under LGPL - original licence link has changed is not relivant.
7704  *
7705  * Fork - LGPL
7706  * <script type="text/javascript">
7707  */
7708
7709 /**
7710  * @class Roo.data.ArrayReader
7711  * @extends Roo.data.DataReader
7712  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7713  * Each element of that Array represents a row of data fields. The
7714  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7715  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7716  * <p>
7717  * Example code:.
7718  * <pre><code>
7719 var RecordDef = Roo.data.Record.create([
7720     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7721     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7722 ]);
7723 var myReader = new Roo.data.ArrayReader({
7724     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7725 }, RecordDef);
7726 </code></pre>
7727  * <p>
7728  * This would consume an Array like this:
7729  * <pre><code>
7730 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7731   </code></pre>
7732  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7733  * @constructor
7734  * Create a new JsonReader
7735  * @param {Object} meta Metadata configuration options.
7736  * @param {Object} recordType Either an Array of field definition objects
7737  * as specified to {@link Roo.data.Record#create},
7738  * or an {@link Roo.data.Record} object
7739  * created using {@link Roo.data.Record#create}.
7740  */
7741 Roo.data.ArrayReader = function(meta, recordType){
7742     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7743 };
7744
7745 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7746     /**
7747      * Create a data block containing Roo.data.Records from an XML document.
7748      * @param {Object} o An Array of row objects which represents the dataset.
7749      * @return {Object} data A data block which is used by an Roo.data.Store object as
7750      * a cache of Roo.data.Records.
7751      */
7752     readRecords : function(o){
7753         var sid = this.meta ? this.meta.id : null;
7754         var recordType = this.recordType, fields = recordType.prototype.fields;
7755         var records = [];
7756         var root = o;
7757             for(var i = 0; i < root.length; i++){
7758                     var n = root[i];
7759                 var values = {};
7760                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7761                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7762                 var f = fields.items[j];
7763                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7764                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7765                 v = f.convert(v);
7766                 values[f.name] = v;
7767             }
7768                 var record = new recordType(values, id);
7769                 record.json = n;
7770                 records[records.length] = record;
7771             }
7772             return {
7773                 records : records,
7774                 totalRecords : records.length
7775             };
7776     }
7777 });/*
7778  * - LGPL
7779  * * 
7780  */
7781
7782 /**
7783  * @class Roo.bootstrap.ComboBox
7784  * @extends Roo.bootstrap.TriggerField
7785  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7786  * @cfg {Boolean} append (true|false) default false
7787  * @constructor
7788  * Create a new ComboBox.
7789  * @param {Object} config Configuration options
7790  */
7791 Roo.bootstrap.ComboBox = function(config){
7792     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7793     this.addEvents({
7794         /**
7795          * @event expand
7796          * Fires when the dropdown list is expanded
7797              * @param {Roo.bootstrap.ComboBox} combo This combo box
7798              */
7799         'expand' : true,
7800         /**
7801          * @event collapse
7802          * Fires when the dropdown list is collapsed
7803              * @param {Roo.bootstrap.ComboBox} combo This combo box
7804              */
7805         'collapse' : true,
7806         /**
7807          * @event beforeselect
7808          * Fires before a list item is selected. Return false to cancel the selection.
7809              * @param {Roo.bootstrap.ComboBox} combo This combo box
7810              * @param {Roo.data.Record} record The data record returned from the underlying store
7811              * @param {Number} index The index of the selected item in the dropdown list
7812              */
7813         'beforeselect' : true,
7814         /**
7815          * @event select
7816          * Fires when a list item is selected
7817              * @param {Roo.bootstrap.ComboBox} combo This combo box
7818              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7819              * @param {Number} index The index of the selected item in the dropdown list
7820              */
7821         'select' : true,
7822         /**
7823          * @event beforequery
7824          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7825          * The event object passed has these properties:
7826              * @param {Roo.bootstrap.ComboBox} combo This combo box
7827              * @param {String} query The query
7828              * @param {Boolean} forceAll true to force "all" query
7829              * @param {Boolean} cancel true to cancel the query
7830              * @param {Object} e The query event object
7831              */
7832         'beforequery': true,
7833          /**
7834          * @event add
7835          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7836              * @param {Roo.bootstrap.ComboBox} combo This combo box
7837              */
7838         'add' : true,
7839         /**
7840          * @event edit
7841          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7842              * @param {Roo.bootstrap.ComboBox} combo This combo box
7843              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7844              */
7845         'edit' : true,
7846         /**
7847          * @event remove
7848          * Fires when the remove value from the combobox array
7849              * @param {Roo.bootstrap.ComboBox} combo This combo box
7850              */
7851         'remove' : true
7852         
7853     });
7854     
7855     
7856     this.selectedIndex = -1;
7857     if(this.mode == 'local'){
7858         if(config.queryDelay === undefined){
7859             this.queryDelay = 10;
7860         }
7861         if(config.minChars === undefined){
7862             this.minChars = 0;
7863         }
7864     }
7865 };
7866
7867 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7868      
7869     /**
7870      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7871      * rendering into an Roo.Editor, defaults to false)
7872      */
7873     /**
7874      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7875      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7876      */
7877     /**
7878      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7879      */
7880     /**
7881      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7882      * the dropdown list (defaults to undefined, with no header element)
7883      */
7884
7885      /**
7886      * @cfg {String/Roo.Template} tpl The template to use to render the output
7887      */
7888      
7889      /**
7890      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7891      */
7892     listWidth: undefined,
7893     /**
7894      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7895      * mode = 'remote' or 'text' if mode = 'local')
7896      */
7897     displayField: undefined,
7898     /**
7899      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7900      * mode = 'remote' or 'value' if mode = 'local'). 
7901      * Note: use of a valueField requires the user make a selection
7902      * in order for a value to be mapped.
7903      */
7904     valueField: undefined,
7905     
7906     
7907     /**
7908      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7909      * field's data value (defaults to the underlying DOM element's name)
7910      */
7911     hiddenName: undefined,
7912     /**
7913      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7914      */
7915     listClass: '',
7916     /**
7917      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7918      */
7919     selectedClass: 'active',
7920     
7921     /**
7922      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7923      */
7924     shadow:'sides',
7925     /**
7926      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7927      * anchor positions (defaults to 'tl-bl')
7928      */
7929     listAlign: 'tl-bl?',
7930     /**
7931      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7932      */
7933     maxHeight: 300,
7934     /**
7935      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7936      * query specified by the allQuery config option (defaults to 'query')
7937      */
7938     triggerAction: 'query',
7939     /**
7940      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7941      * (defaults to 4, does not apply if editable = false)
7942      */
7943     minChars : 4,
7944     /**
7945      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7946      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7947      */
7948     typeAhead: false,
7949     /**
7950      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7951      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7952      */
7953     queryDelay: 500,
7954     /**
7955      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7956      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7957      */
7958     pageSize: 0,
7959     /**
7960      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7961      * when editable = true (defaults to false)
7962      */
7963     selectOnFocus:false,
7964     /**
7965      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7966      */
7967     queryParam: 'query',
7968     /**
7969      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7970      * when mode = 'remote' (defaults to 'Loading...')
7971      */
7972     loadingText: 'Loading...',
7973     /**
7974      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7975      */
7976     resizable: false,
7977     /**
7978      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7979      */
7980     handleHeight : 8,
7981     /**
7982      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7983      * traditional select (defaults to true)
7984      */
7985     editable: true,
7986     /**
7987      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7988      */
7989     allQuery: '',
7990     /**
7991      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7992      */
7993     mode: 'remote',
7994     /**
7995      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7996      * listWidth has a higher value)
7997      */
7998     minListWidth : 70,
7999     /**
8000      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8001      * allow the user to set arbitrary text into the field (defaults to false)
8002      */
8003     forceSelection:false,
8004     /**
8005      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8006      * if typeAhead = true (defaults to 250)
8007      */
8008     typeAheadDelay : 250,
8009     /**
8010      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8011      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8012      */
8013     valueNotFoundText : undefined,
8014     /**
8015      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8016      */
8017     blockFocus : false,
8018     
8019     /**
8020      * @cfg {Boolean} disableClear Disable showing of clear button.
8021      */
8022     disableClear : false,
8023     /**
8024      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8025      */
8026     alwaysQuery : false,
8027     
8028     /**
8029      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8030      */
8031     multiple : false,
8032     
8033     //private
8034     addicon : false,
8035     editicon: false,
8036     
8037     page: 0,
8038     hasQuery: false,
8039     append: false,
8040     loadNext: false,
8041     item: [],
8042     
8043     // element that contains real text value.. (when hidden is used..)
8044      
8045     // private
8046     initEvents: function(){
8047         
8048         if (!this.store) {
8049             throw "can not find store for combo";
8050         }
8051         this.store = Roo.factory(this.store, Roo.data);
8052         
8053         
8054         
8055         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8056         
8057         
8058         if(this.hiddenName){
8059             
8060             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8061             
8062             this.hiddenField.dom.value =
8063                 this.hiddenValue !== undefined ? this.hiddenValue :
8064                 this.value !== undefined ? this.value : '';
8065
8066             // prevent input submission
8067             this.el.dom.removeAttribute('name');
8068             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8069              
8070              
8071         }
8072         //if(Roo.isGecko){
8073         //    this.el.dom.setAttribute('autocomplete', 'off');
8074         //}
8075
8076         var cls = 'x-combo-list';
8077         this.list = this.el.select('ul.dropdown-menu',true).first();
8078
8079         //this.list = new Roo.Layer({
8080         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8081         //});
8082         
8083         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8084         this.list.setWidth(lw);
8085         
8086         this.list.on('mouseover', this.onViewOver, this);
8087         this.list.on('mousemove', this.onViewMove, this);
8088         
8089         this.list.on('scroll', this.onViewScroll, this);
8090         
8091         /*
8092         this.list.swallowEvent('mousewheel');
8093         this.assetHeight = 0;
8094
8095         if(this.title){
8096             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8097             this.assetHeight += this.header.getHeight();
8098         }
8099
8100         this.innerList = this.list.createChild({cls:cls+'-inner'});
8101         this.innerList.on('mouseover', this.onViewOver, this);
8102         this.innerList.on('mousemove', this.onViewMove, this);
8103         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8104         
8105         if(this.allowBlank && !this.pageSize && !this.disableClear){
8106             this.footer = this.list.createChild({cls:cls+'-ft'});
8107             this.pageTb = new Roo.Toolbar(this.footer);
8108            
8109         }
8110         if(this.pageSize){
8111             this.footer = this.list.createChild({cls:cls+'-ft'});
8112             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8113                     {pageSize: this.pageSize});
8114             
8115         }
8116         
8117         if (this.pageTb && this.allowBlank && !this.disableClear) {
8118             var _this = this;
8119             this.pageTb.add(new Roo.Toolbar.Fill(), {
8120                 cls: 'x-btn-icon x-btn-clear',
8121                 text: '&#160;',
8122                 handler: function()
8123                 {
8124                     _this.collapse();
8125                     _this.clearValue();
8126                     _this.onSelect(false, -1);
8127                 }
8128             });
8129         }
8130         if (this.footer) {
8131             this.assetHeight += this.footer.getHeight();
8132         }
8133         */
8134             
8135         if(!this.tpl){
8136             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8137         }
8138
8139         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8140             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8141         });
8142         //this.view.wrapEl.setDisplayed(false);
8143         this.view.on('click', this.onViewClick, this);
8144         
8145         
8146         
8147         this.store.on('beforeload', this.onBeforeLoad, this);
8148         this.store.on('load', this.onLoad, this);
8149         this.store.on('loadexception', this.onLoadException, this);
8150         /*
8151         if(this.resizable){
8152             this.resizer = new Roo.Resizable(this.list,  {
8153                pinned:true, handles:'se'
8154             });
8155             this.resizer.on('resize', function(r, w, h){
8156                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8157                 this.listWidth = w;
8158                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8159                 this.restrictHeight();
8160             }, this);
8161             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8162         }
8163         */
8164         if(!this.editable){
8165             this.editable = true;
8166             this.setEditable(false);
8167         }
8168         
8169         /*
8170         
8171         if (typeof(this.events.add.listeners) != 'undefined') {
8172             
8173             this.addicon = this.wrap.createChild(
8174                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8175        
8176             this.addicon.on('click', function(e) {
8177                 this.fireEvent('add', this);
8178             }, this);
8179         }
8180         if (typeof(this.events.edit.listeners) != 'undefined') {
8181             
8182             this.editicon = this.wrap.createChild(
8183                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8184             if (this.addicon) {
8185                 this.editicon.setStyle('margin-left', '40px');
8186             }
8187             this.editicon.on('click', function(e) {
8188                 
8189                 // we fire even  if inothing is selected..
8190                 this.fireEvent('edit', this, this.lastData );
8191                 
8192             }, this);
8193         }
8194         */
8195         
8196         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8197             "up" : function(e){
8198                 this.inKeyMode = true;
8199                 this.selectPrev();
8200             },
8201
8202             "down" : function(e){
8203                 if(!this.isExpanded()){
8204                     this.onTriggerClick();
8205                 }else{
8206                     this.inKeyMode = true;
8207                     this.selectNext();
8208                 }
8209             },
8210
8211             "enter" : function(e){
8212                 this.onViewClick();
8213                 //return true;
8214             },
8215
8216             "esc" : function(e){
8217                 this.collapse();
8218             },
8219
8220             "tab" : function(e){
8221                 this.collapse();
8222                 
8223                 if(this.fireEvent("specialkey", this, e)){
8224                     this.onViewClick(false);
8225                 }
8226                 
8227                 return true;
8228             },
8229
8230             scope : this,
8231
8232             doRelay : function(foo, bar, hname){
8233                 if(hname == 'down' || this.scope.isExpanded()){
8234                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8235                 }
8236                 return true;
8237             },
8238
8239             forceKeyDown: true
8240         });
8241         
8242         
8243         this.queryDelay = Math.max(this.queryDelay || 10,
8244                 this.mode == 'local' ? 10 : 250);
8245         
8246         
8247         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8248         
8249         if(this.typeAhead){
8250             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8251         }
8252         if(this.editable !== false){
8253             this.inputEl().on("keyup", this.onKeyUp, this);
8254         }
8255         if(this.forceSelection){
8256             this.on('blur', this.doForce, this);
8257         }
8258         
8259         if(this.multiple){
8260             this.choices = this.el.select('ul.select2-choices', true).first();
8261             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8262         }
8263     },
8264
8265     onDestroy : function(){
8266         if(this.view){
8267             this.view.setStore(null);
8268             this.view.el.removeAllListeners();
8269             this.view.el.remove();
8270             this.view.purgeListeners();
8271         }
8272         if(this.list){
8273             this.list.dom.innerHTML  = '';
8274         }
8275         if(this.store){
8276             this.store.un('beforeload', this.onBeforeLoad, this);
8277             this.store.un('load', this.onLoad, this);
8278             this.store.un('loadexception', this.onLoadException, this);
8279         }
8280         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8281     },
8282
8283     // private
8284     fireKey : function(e){
8285         if(e.isNavKeyPress() && !this.list.isVisible()){
8286             this.fireEvent("specialkey", this, e);
8287         }
8288     },
8289
8290     // private
8291     onResize: function(w, h){
8292 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8293 //        
8294 //        if(typeof w != 'number'){
8295 //            // we do not handle it!?!?
8296 //            return;
8297 //        }
8298 //        var tw = this.trigger.getWidth();
8299 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8300 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8301 //        var x = w - tw;
8302 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8303 //            
8304 //        //this.trigger.setStyle('left', x+'px');
8305 //        
8306 //        if(this.list && this.listWidth === undefined){
8307 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8308 //            this.list.setWidth(lw);
8309 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8310 //        }
8311         
8312     
8313         
8314     },
8315
8316     /**
8317      * Allow or prevent the user from directly editing the field text.  If false is passed,
8318      * the user will only be able to select from the items defined in the dropdown list.  This method
8319      * is the runtime equivalent of setting the 'editable' config option at config time.
8320      * @param {Boolean} value True to allow the user to directly edit the field text
8321      */
8322     setEditable : function(value){
8323         if(value == this.editable){
8324             return;
8325         }
8326         this.editable = value;
8327         if(!value){
8328             this.inputEl().dom.setAttribute('readOnly', true);
8329             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8330             this.inputEl().addClass('x-combo-noedit');
8331         }else{
8332             this.inputEl().dom.setAttribute('readOnly', false);
8333             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8334             this.inputEl().removeClass('x-combo-noedit');
8335         }
8336     },
8337
8338     // private
8339     
8340     onBeforeLoad : function(combo,opts){
8341         if(!this.hasFocus){
8342             return;
8343         }
8344          if (!opts.add) {
8345             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8346          }
8347         this.restrictHeight();
8348         this.selectedIndex = -1;
8349     },
8350
8351     // private
8352     onLoad : function(){
8353         
8354         this.hasQuery = false;
8355         
8356         if(!this.hasFocus){
8357             return;
8358         }
8359         
8360         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8361             this.loading.hide();
8362         }
8363         
8364         if(this.store.getCount() > 0){
8365             this.expand();
8366             this.restrictHeight();
8367             if(this.lastQuery == this.allQuery){
8368                 if(this.editable){
8369                     this.inputEl().dom.select();
8370                 }
8371                 if(!this.selectByValue(this.value, true)){
8372                     this.select(0, true);
8373                 }
8374             }else{
8375                 this.selectNext();
8376                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8377                     this.taTask.delay(this.typeAheadDelay);
8378                 }
8379             }
8380         }else{
8381             this.onEmptyResults();
8382         }
8383         
8384         //this.el.focus();
8385     },
8386     // private
8387     onLoadException : function()
8388     {
8389         this.hasQuery = false;
8390         
8391         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8392             this.loading.hide();
8393         }
8394         
8395         this.collapse();
8396         Roo.log(this.store.reader.jsonData);
8397         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8398             // fixme
8399             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8400         }
8401         
8402         
8403     },
8404     // private
8405     onTypeAhead : function(){
8406         if(this.store.getCount() > 0){
8407             var r = this.store.getAt(0);
8408             var newValue = r.data[this.displayField];
8409             var len = newValue.length;
8410             var selStart = this.getRawValue().length;
8411             
8412             if(selStart != len){
8413                 this.setRawValue(newValue);
8414                 this.selectText(selStart, newValue.length);
8415             }
8416         }
8417     },
8418
8419     // private
8420     onSelect : function(record, index){
8421         
8422         if(this.fireEvent('beforeselect', this, record, index) !== false){
8423         
8424             this.setFromData(index > -1 ? record.data : false);
8425             
8426             this.collapse();
8427             this.fireEvent('select', this, record, index);
8428         }
8429     },
8430
8431     /**
8432      * Returns the currently selected field value or empty string if no value is set.
8433      * @return {String} value The selected value
8434      */
8435     getValue : function(){
8436         
8437         if(this.multiple){
8438             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8439         }
8440         
8441         if(this.valueField){
8442             return typeof this.value != 'undefined' ? this.value : '';
8443         }else{
8444             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8445         }
8446     },
8447
8448     /**
8449      * Clears any text/value currently set in the field
8450      */
8451     clearValue : function(){
8452         if(this.hiddenField){
8453             this.hiddenField.dom.value = '';
8454         }
8455         this.value = '';
8456         this.setRawValue('');
8457         this.lastSelectionText = '';
8458         
8459     },
8460
8461     /**
8462      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8463      * will be displayed in the field.  If the value does not match the data value of an existing item,
8464      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8465      * Otherwise the field will be blank (although the value will still be set).
8466      * @param {String} value The value to match
8467      */
8468     setValue : function(v){
8469         if(this.multiple){
8470             this.syncValue();
8471             return;
8472         }
8473         
8474         var text = v;
8475         if(this.valueField){
8476             var r = this.findRecord(this.valueField, v);
8477             if(r){
8478                 text = r.data[this.displayField];
8479             }else if(this.valueNotFoundText !== undefined){
8480                 text = this.valueNotFoundText;
8481             }
8482         }
8483         this.lastSelectionText = text;
8484         if(this.hiddenField){
8485             this.hiddenField.dom.value = v;
8486         }
8487         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8488         this.value = v;
8489     },
8490     /**
8491      * @property {Object} the last set data for the element
8492      */
8493     
8494     lastData : false,
8495     /**
8496      * Sets the value of the field based on a object which is related to the record format for the store.
8497      * @param {Object} value the value to set as. or false on reset?
8498      */
8499     setFromData : function(o){
8500         
8501         if(this.multiple){
8502             this.addItem(o);
8503             return;
8504         }
8505             
8506         var dv = ''; // display value
8507         var vv = ''; // value value..
8508         this.lastData = o;
8509         if (this.displayField) {
8510             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8511         } else {
8512             // this is an error condition!!!
8513             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8514         }
8515         
8516         if(this.valueField){
8517             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8518         }
8519         
8520         if(this.hiddenField){
8521             this.hiddenField.dom.value = vv;
8522             
8523             this.lastSelectionText = dv;
8524             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8525             this.value = vv;
8526             return;
8527         }
8528         // no hidden field.. - we store the value in 'value', but still display
8529         // display field!!!!
8530         this.lastSelectionText = dv;
8531         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8532         this.value = vv;
8533         
8534         
8535     },
8536     // private
8537     reset : function(){
8538         // overridden so that last data is reset..
8539         this.setValue(this.originalValue);
8540         this.clearInvalid();
8541         this.lastData = false;
8542         if (this.view) {
8543             this.view.clearSelections();
8544         }
8545     },
8546     // private
8547     findRecord : function(prop, value){
8548         var record;
8549         if(this.store.getCount() > 0){
8550             this.store.each(function(r){
8551                 if(r.data[prop] == value){
8552                     record = r;
8553                     return false;
8554                 }
8555                 return true;
8556             });
8557         }
8558         return record;
8559     },
8560     
8561     getName: function()
8562     {
8563         // returns hidden if it's set..
8564         if (!this.rendered) {return ''};
8565         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8566         
8567     },
8568     // private
8569     onViewMove : function(e, t){
8570         this.inKeyMode = false;
8571     },
8572
8573     // private
8574     onViewOver : function(e, t){
8575         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8576             return;
8577         }
8578         var item = this.view.findItemFromChild(t);
8579         if(item){
8580             var index = this.view.indexOf(item);
8581             this.select(index, false);
8582         }
8583     },
8584
8585     // private
8586     onViewClick : function(doFocus)
8587     {
8588         var index = this.view.getSelectedIndexes()[0];
8589         var r = this.store.getAt(index);
8590         if(r){
8591             this.onSelect(r, index);
8592         }
8593         if(doFocus !== false && !this.blockFocus){
8594             this.inputEl().focus();
8595         }
8596     },
8597
8598     // private
8599     restrictHeight : function(){
8600         //this.innerList.dom.style.height = '';
8601         //var inner = this.innerList.dom;
8602         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8603         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8604         //this.list.beginUpdate();
8605         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8606         this.list.alignTo(this.inputEl(), this.listAlign);
8607         //this.list.endUpdate();
8608     },
8609
8610     // private
8611     onEmptyResults : function(){
8612         this.collapse();
8613     },
8614
8615     /**
8616      * Returns true if the dropdown list is expanded, else false.
8617      */
8618     isExpanded : function(){
8619         return this.list.isVisible();
8620     },
8621
8622     /**
8623      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8624      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8625      * @param {String} value The data value of the item to select
8626      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8627      * selected item if it is not currently in view (defaults to true)
8628      * @return {Boolean} True if the value matched an item in the list, else false
8629      */
8630     selectByValue : function(v, scrollIntoView){
8631         if(v !== undefined && v !== null){
8632             var r = this.findRecord(this.valueField || this.displayField, v);
8633             if(r){
8634                 this.select(this.store.indexOf(r), scrollIntoView);
8635                 return true;
8636             }
8637         }
8638         return false;
8639     },
8640
8641     /**
8642      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8643      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8644      * @param {Number} index The zero-based index of the list item to select
8645      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8646      * selected item if it is not currently in view (defaults to true)
8647      */
8648     select : function(index, scrollIntoView){
8649         this.selectedIndex = index;
8650         this.view.select(index);
8651         if(scrollIntoView !== false){
8652             var el = this.view.getNode(index);
8653             if(el){
8654                 //this.innerList.scrollChildIntoView(el, false);
8655                 
8656             }
8657         }
8658     },
8659
8660     // private
8661     selectNext : function(){
8662         var ct = this.store.getCount();
8663         if(ct > 0){
8664             if(this.selectedIndex == -1){
8665                 this.select(0);
8666             }else if(this.selectedIndex < ct-1){
8667                 this.select(this.selectedIndex+1);
8668             }
8669         }
8670     },
8671
8672     // private
8673     selectPrev : function(){
8674         var ct = this.store.getCount();
8675         if(ct > 0){
8676             if(this.selectedIndex == -1){
8677                 this.select(0);
8678             }else if(this.selectedIndex != 0){
8679                 this.select(this.selectedIndex-1);
8680             }
8681         }
8682     },
8683
8684     // private
8685     onKeyUp : function(e){
8686         if(this.editable !== false && !e.isSpecialKey()){
8687             this.lastKey = e.getKey();
8688             this.dqTask.delay(this.queryDelay);
8689         }
8690     },
8691
8692     // private
8693     validateBlur : function(){
8694         return !this.list || !this.list.isVisible();   
8695     },
8696
8697     // private
8698     initQuery : function(){
8699         this.doQuery(this.getRawValue());
8700     },
8701
8702     // private
8703     doForce : function(){
8704         if(this.el.dom.value.length > 0){
8705             this.el.dom.value =
8706                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8707              
8708         }
8709     },
8710
8711     /**
8712      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8713      * query allowing the query action to be canceled if needed.
8714      * @param {String} query The SQL query to execute
8715      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8716      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8717      * saved in the current store (defaults to false)
8718      */
8719     doQuery : function(q, forceAll){
8720         
8721         if(q === undefined || q === null){
8722             q = '';
8723         }
8724         var qe = {
8725             query: q,
8726             forceAll: forceAll,
8727             combo: this,
8728             cancel:false
8729         };
8730         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8731             return false;
8732         }
8733         q = qe.query;
8734         
8735         forceAll = qe.forceAll;
8736         if(forceAll === true || (q.length >= this.minChars)){
8737             
8738             this.hasQuery = true;
8739             
8740             if(this.lastQuery != q || this.alwaysQuery){
8741                 this.lastQuery = q;
8742                 if(this.mode == 'local'){
8743                     this.selectedIndex = -1;
8744                     if(forceAll){
8745                         this.store.clearFilter();
8746                     }else{
8747                         this.store.filter(this.displayField, q);
8748                     }
8749                     this.onLoad();
8750                 }else{
8751                     this.store.baseParams[this.queryParam] = q;
8752                     
8753                     var options = {params : this.getParams(q)};
8754                     
8755                     if(this.loadNext){
8756                         options.add = true;
8757                         options.params.start = this.page * this.pageSize;
8758                     }
8759                     
8760                     this.store.load(options);
8761                     this.expand();
8762                 }
8763             }else{
8764                 this.selectedIndex = -1;
8765                 this.onLoad();   
8766             }
8767         }
8768         
8769         this.loadNext = false;
8770     },
8771
8772     // private
8773     getParams : function(q){
8774         var p = {};
8775         //p[this.queryParam] = q;
8776         
8777         if(this.pageSize){
8778             p.start = 0;
8779             p.limit = this.pageSize;
8780         }
8781         return p;
8782     },
8783
8784     /**
8785      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8786      */
8787     collapse : function(){
8788         if(!this.isExpanded()){
8789             return;
8790         }
8791         
8792         this.list.hide();
8793         Roo.get(document).un('mousedown', this.collapseIf, this);
8794         Roo.get(document).un('mousewheel', this.collapseIf, this);
8795         if (!this.editable) {
8796             Roo.get(document).un('keydown', this.listKeyPress, this);
8797         }
8798         this.fireEvent('collapse', this);
8799     },
8800
8801     // private
8802     collapseIf : function(e){
8803         var in_combo  = e.within(this.el);
8804         var in_list =  e.within(this.list);
8805         
8806         if (in_combo || in_list) {
8807             //e.stopPropagation();
8808             return;
8809         }
8810
8811         this.collapse();
8812         
8813     },
8814
8815     /**
8816      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8817      */
8818     expand : function(){
8819        
8820         if(this.isExpanded() || !this.hasFocus){
8821             return;
8822         }
8823          Roo.log('expand');
8824         this.list.alignTo(this.inputEl(), this.listAlign);
8825         this.list.show();
8826         Roo.get(document).on('mousedown', this.collapseIf, this);
8827         Roo.get(document).on('mousewheel', this.collapseIf, this);
8828         if (!this.editable) {
8829             Roo.get(document).on('keydown', this.listKeyPress, this);
8830         }
8831         
8832         this.fireEvent('expand', this);
8833     },
8834
8835     // private
8836     // Implements the default empty TriggerField.onTriggerClick function
8837     onTriggerClick : function()
8838     {
8839         Roo.log('trigger click');
8840         
8841         if(this.disabled){
8842             return;
8843         }
8844         
8845         this.page = 0;
8846         this.loadNext = false;
8847         
8848         if(this.isExpanded()){
8849             this.collapse();
8850             if (!this.blockFocus) {
8851                 this.inputEl().focus();
8852             }
8853             
8854         }else {
8855             this.hasFocus = true;
8856             if(this.triggerAction == 'all') {
8857                 this.doQuery(this.allQuery, true);
8858             } else {
8859                 this.doQuery(this.getRawValue());
8860             }
8861             if (!this.blockFocus) {
8862                 this.inputEl().focus();
8863             }
8864         }
8865     },
8866     listKeyPress : function(e)
8867     {
8868         //Roo.log('listkeypress');
8869         // scroll to first matching element based on key pres..
8870         if (e.isSpecialKey()) {
8871             return false;
8872         }
8873         var k = String.fromCharCode(e.getKey()).toUpperCase();
8874         //Roo.log(k);
8875         var match  = false;
8876         var csel = this.view.getSelectedNodes();
8877         var cselitem = false;
8878         if (csel.length) {
8879             var ix = this.view.indexOf(csel[0]);
8880             cselitem  = this.store.getAt(ix);
8881             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8882                 cselitem = false;
8883             }
8884             
8885         }
8886         
8887         this.store.each(function(v) { 
8888             if (cselitem) {
8889                 // start at existing selection.
8890                 if (cselitem.id == v.id) {
8891                     cselitem = false;
8892                 }
8893                 return true;
8894             }
8895                 
8896             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8897                 match = this.store.indexOf(v);
8898                 return false;
8899             }
8900             return true;
8901         }, this);
8902         
8903         if (match === false) {
8904             return true; // no more action?
8905         }
8906         // scroll to?
8907         this.view.select(match);
8908         var sn = Roo.get(this.view.getSelectedNodes()[0])
8909         //sn.scrollIntoView(sn.dom.parentNode, false);
8910     },
8911     
8912     onViewScroll : function(e, t){
8913         
8914         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8915             return;
8916         }
8917         
8918         this.hasQuery = true;
8919         
8920         this.loading = this.list.select('.loading', true).first();
8921         
8922         if(this.loading === null){
8923             this.list.createChild({
8924                 tag: 'div',
8925                 cls: 'loading select2-more-results select2-active',
8926                 html: 'Loading more results...'
8927             })
8928             
8929             this.loading = this.list.select('.loading', true).first();
8930             
8931             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8932             
8933             this.loading.hide();
8934         }
8935         
8936         this.loading.show();
8937         
8938         var _combo = this;
8939         
8940         this.page++;
8941         this.loadNext = true;
8942         
8943         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8944         
8945         return;
8946     },
8947     
8948     addItem : function(o)
8949     {   
8950         var dv = ''; // display value
8951         
8952         if (this.displayField) {
8953             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8954         } else {
8955             // this is an error condition!!!
8956             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8957         }
8958         
8959         if(!dv.length){
8960             return;
8961         }
8962         
8963         var choice = this.choices.createChild({
8964             tag: 'li',
8965             cls: 'select2-search-choice',
8966             cn: [
8967                 {
8968                     tag: 'div',
8969                     html: dv
8970                 },
8971                 {
8972                     tag: 'a',
8973                     href: '#',
8974                     cls: 'select2-search-choice-close',
8975                     tabindex: '-1'
8976                 }
8977             ]
8978             
8979         }, this.searchField);
8980         
8981         var close = choice.select('a.select2-search-choice-close', true).first()
8982         
8983         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8984         
8985         this.item.push(o);
8986         this.lastData = o;
8987         
8988         this.syncValue();
8989         
8990         this.inputEl().dom.value = '';
8991         
8992     },
8993     
8994     onRemoveItem : function(e, _self, o)
8995     {
8996         Roo.log('remove item');
8997         var index = this.item.indexOf(o.data) * 1;
8998         
8999         if( index < 0){
9000             Roo.log('not this item?!');
9001             return;
9002         }
9003         
9004         this.item.splice(index, 1);
9005         o.item.remove();
9006         
9007         this.syncValue();
9008         
9009         this.fireEvent('remove', this);
9010         
9011     },
9012     
9013     syncValue : function()
9014     {
9015         if(!this.item.length){
9016             this.clearValue();
9017             return;
9018         }
9019             
9020         var value = [];
9021         var _this = this;
9022         Roo.each(this.item, function(i){
9023             if(_this.valueField){
9024                 value.push(i[_this.valueField]);
9025                 return;
9026             }
9027
9028             value.push(i);
9029         });
9030
9031         this.value = value.join(',');
9032
9033         if(this.hiddenField){
9034             this.hiddenField.dom.value = this.value;
9035         }
9036     },
9037     
9038     clearItem : function()
9039     {
9040         if(!this.multiple){
9041             return;
9042         }
9043         
9044         this.item = [];
9045         
9046         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9047            c.remove();
9048         });
9049         
9050         this.syncValue();
9051     }
9052     
9053     
9054
9055     /** 
9056     * @cfg {Boolean} grow 
9057     * @hide 
9058     */
9059     /** 
9060     * @cfg {Number} growMin 
9061     * @hide 
9062     */
9063     /** 
9064     * @cfg {Number} growMax 
9065     * @hide 
9066     */
9067     /**
9068      * @hide
9069      * @method autoSize
9070      */
9071 });
9072 /*
9073  * Based on:
9074  * Ext JS Library 1.1.1
9075  * Copyright(c) 2006-2007, Ext JS, LLC.
9076  *
9077  * Originally Released Under LGPL - original licence link has changed is not relivant.
9078  *
9079  * Fork - LGPL
9080  * <script type="text/javascript">
9081  */
9082
9083 /**
9084  * @class Roo.View
9085  * @extends Roo.util.Observable
9086  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9087  * This class also supports single and multi selection modes. <br>
9088  * Create a data model bound view:
9089  <pre><code>
9090  var store = new Roo.data.Store(...);
9091
9092  var view = new Roo.View({
9093     el : "my-element",
9094     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9095  
9096     singleSelect: true,
9097     selectedClass: "ydataview-selected",
9098     store: store
9099  });
9100
9101  // listen for node click?
9102  view.on("click", function(vw, index, node, e){
9103  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9104  });
9105
9106  // load XML data
9107  dataModel.load("foobar.xml");
9108  </code></pre>
9109  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9110  * <br><br>
9111  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9112  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9113  * 
9114  * Note: old style constructor is still suported (container, template, config)
9115  * 
9116  * @constructor
9117  * Create a new View
9118  * @param {Object} config The config object
9119  * 
9120  */
9121 Roo.View = function(config, depreciated_tpl, depreciated_config){
9122     
9123     if (typeof(depreciated_tpl) == 'undefined') {
9124         // new way.. - universal constructor.
9125         Roo.apply(this, config);
9126         this.el  = Roo.get(this.el);
9127     } else {
9128         // old format..
9129         this.el  = Roo.get(config);
9130         this.tpl = depreciated_tpl;
9131         Roo.apply(this, depreciated_config);
9132     }
9133     this.wrapEl  = this.el.wrap().wrap();
9134     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9135     
9136     
9137     if(typeof(this.tpl) == "string"){
9138         this.tpl = new Roo.Template(this.tpl);
9139     } else {
9140         // support xtype ctors..
9141         this.tpl = new Roo.factory(this.tpl, Roo);
9142     }
9143     
9144     
9145     this.tpl.compile();
9146    
9147   
9148     
9149      
9150     /** @private */
9151     this.addEvents({
9152         /**
9153          * @event beforeclick
9154          * Fires before a click is processed. Returns false to cancel the default action.
9155          * @param {Roo.View} this
9156          * @param {Number} index The index of the target node
9157          * @param {HTMLElement} node The target node
9158          * @param {Roo.EventObject} e The raw event object
9159          */
9160             "beforeclick" : true,
9161         /**
9162          * @event click
9163          * Fires when a template node is clicked.
9164          * @param {Roo.View} this
9165          * @param {Number} index The index of the target node
9166          * @param {HTMLElement} node The target node
9167          * @param {Roo.EventObject} e The raw event object
9168          */
9169             "click" : true,
9170         /**
9171          * @event dblclick
9172          * Fires when a template node is double clicked.
9173          * @param {Roo.View} this
9174          * @param {Number} index The index of the target node
9175          * @param {HTMLElement} node The target node
9176          * @param {Roo.EventObject} e The raw event object
9177          */
9178             "dblclick" : true,
9179         /**
9180          * @event contextmenu
9181          * Fires when a template node is right clicked.
9182          * @param {Roo.View} this
9183          * @param {Number} index The index of the target node
9184          * @param {HTMLElement} node The target node
9185          * @param {Roo.EventObject} e The raw event object
9186          */
9187             "contextmenu" : true,
9188         /**
9189          * @event selectionchange
9190          * Fires when the selected nodes change.
9191          * @param {Roo.View} this
9192          * @param {Array} selections Array of the selected nodes
9193          */
9194             "selectionchange" : true,
9195     
9196         /**
9197          * @event beforeselect
9198          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9199          * @param {Roo.View} this
9200          * @param {HTMLElement} node The node to be selected
9201          * @param {Array} selections Array of currently selected nodes
9202          */
9203             "beforeselect" : true,
9204         /**
9205          * @event preparedata
9206          * Fires on every row to render, to allow you to change the data.
9207          * @param {Roo.View} this
9208          * @param {Object} data to be rendered (change this)
9209          */
9210           "preparedata" : true
9211           
9212           
9213         });
9214
9215
9216
9217     this.el.on({
9218         "click": this.onClick,
9219         "dblclick": this.onDblClick,
9220         "contextmenu": this.onContextMenu,
9221         scope:this
9222     });
9223
9224     this.selections = [];
9225     this.nodes = [];
9226     this.cmp = new Roo.CompositeElementLite([]);
9227     if(this.store){
9228         this.store = Roo.factory(this.store, Roo.data);
9229         this.setStore(this.store, true);
9230     }
9231     
9232     if ( this.footer && this.footer.xtype) {
9233            
9234          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9235         
9236         this.footer.dataSource = this.store
9237         this.footer.container = fctr;
9238         this.footer = Roo.factory(this.footer, Roo);
9239         fctr.insertFirst(this.el);
9240         
9241         // this is a bit insane - as the paging toolbar seems to detach the el..
9242 //        dom.parentNode.parentNode.parentNode
9243          // they get detached?
9244     }
9245     
9246     
9247     Roo.View.superclass.constructor.call(this);
9248     
9249     
9250 };
9251
9252 Roo.extend(Roo.View, Roo.util.Observable, {
9253     
9254      /**
9255      * @cfg {Roo.data.Store} store Data store to load data from.
9256      */
9257     store : false,
9258     
9259     /**
9260      * @cfg {String|Roo.Element} el The container element.
9261      */
9262     el : '',
9263     
9264     /**
9265      * @cfg {String|Roo.Template} tpl The template used by this View 
9266      */
9267     tpl : false,
9268     /**
9269      * @cfg {String} dataName the named area of the template to use as the data area
9270      *                          Works with domtemplates roo-name="name"
9271      */
9272     dataName: false,
9273     /**
9274      * @cfg {String} selectedClass The css class to add to selected nodes
9275      */
9276     selectedClass : "x-view-selected",
9277      /**
9278      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9279      */
9280     emptyText : "",
9281     
9282     /**
9283      * @cfg {String} text to display on mask (default Loading)
9284      */
9285     mask : false,
9286     /**
9287      * @cfg {Boolean} multiSelect Allow multiple selection
9288      */
9289     multiSelect : false,
9290     /**
9291      * @cfg {Boolean} singleSelect Allow single selection
9292      */
9293     singleSelect:  false,
9294     
9295     /**
9296      * @cfg {Boolean} toggleSelect - selecting 
9297      */
9298     toggleSelect : false,
9299     
9300     /**
9301      * Returns the element this view is bound to.
9302      * @return {Roo.Element}
9303      */
9304     getEl : function(){
9305         return this.wrapEl;
9306     },
9307     
9308     
9309
9310     /**
9311      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9312      */
9313     refresh : function(){
9314         Roo.log('refresh');
9315         var t = this.tpl;
9316         
9317         // if we are using something like 'domtemplate', then
9318         // the what gets used is:
9319         // t.applySubtemplate(NAME, data, wrapping data..)
9320         // the outer template then get' applied with
9321         //     the store 'extra data'
9322         // and the body get's added to the
9323         //      roo-name="data" node?
9324         //      <span class='roo-tpl-{name}'></span> ?????
9325         
9326         
9327         
9328         this.clearSelections();
9329         this.el.update("");
9330         var html = [];
9331         var records = this.store.getRange();
9332         if(records.length < 1) {
9333             
9334             // is this valid??  = should it render a template??
9335             
9336             this.el.update(this.emptyText);
9337             return;
9338         }
9339         var el = this.el;
9340         if (this.dataName) {
9341             this.el.update(t.apply(this.store.meta)); //????
9342             el = this.el.child('.roo-tpl-' + this.dataName);
9343         }
9344         
9345         for(var i = 0, len = records.length; i < len; i++){
9346             var data = this.prepareData(records[i].data, i, records[i]);
9347             this.fireEvent("preparedata", this, data, i, records[i]);
9348             html[html.length] = Roo.util.Format.trim(
9349                 this.dataName ?
9350                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9351                     t.apply(data)
9352             );
9353         }
9354         
9355         
9356         
9357         el.update(html.join(""));
9358         this.nodes = el.dom.childNodes;
9359         this.updateIndexes(0);
9360     },
9361     
9362
9363     /**
9364      * Function to override to reformat the data that is sent to
9365      * the template for each node.
9366      * DEPRICATED - use the preparedata event handler.
9367      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9368      * a JSON object for an UpdateManager bound view).
9369      */
9370     prepareData : function(data, index, record)
9371     {
9372         this.fireEvent("preparedata", this, data, index, record);
9373         return data;
9374     },
9375
9376     onUpdate : function(ds, record){
9377          Roo.log('on update');   
9378         this.clearSelections();
9379         var index = this.store.indexOf(record);
9380         var n = this.nodes[index];
9381         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9382         n.parentNode.removeChild(n);
9383         this.updateIndexes(index, index);
9384     },
9385
9386     
9387     
9388 // --------- FIXME     
9389     onAdd : function(ds, records, index)
9390     {
9391         Roo.log(['on Add', ds, records, index] );        
9392         this.clearSelections();
9393         if(this.nodes.length == 0){
9394             this.refresh();
9395             return;
9396         }
9397         var n = this.nodes[index];
9398         for(var i = 0, len = records.length; i < len; i++){
9399             var d = this.prepareData(records[i].data, i, records[i]);
9400             if(n){
9401                 this.tpl.insertBefore(n, d);
9402             }else{
9403                 
9404                 this.tpl.append(this.el, d);
9405             }
9406         }
9407         this.updateIndexes(index);
9408     },
9409
9410     onRemove : function(ds, record, index){
9411         Roo.log('onRemove');
9412         this.clearSelections();
9413         var el = this.dataName  ?
9414             this.el.child('.roo-tpl-' + this.dataName) :
9415             this.el; 
9416         
9417         el.dom.removeChild(this.nodes[index]);
9418         this.updateIndexes(index);
9419     },
9420
9421     /**
9422      * Refresh an individual node.
9423      * @param {Number} index
9424      */
9425     refreshNode : function(index){
9426         this.onUpdate(this.store, this.store.getAt(index));
9427     },
9428
9429     updateIndexes : function(startIndex, endIndex){
9430         var ns = this.nodes;
9431         startIndex = startIndex || 0;
9432         endIndex = endIndex || ns.length - 1;
9433         for(var i = startIndex; i <= endIndex; i++){
9434             ns[i].nodeIndex = i;
9435         }
9436     },
9437
9438     /**
9439      * Changes the data store this view uses and refresh the view.
9440      * @param {Store} store
9441      */
9442     setStore : function(store, initial){
9443         if(!initial && this.store){
9444             this.store.un("datachanged", this.refresh);
9445             this.store.un("add", this.onAdd);
9446             this.store.un("remove", this.onRemove);
9447             this.store.un("update", this.onUpdate);
9448             this.store.un("clear", this.refresh);
9449             this.store.un("beforeload", this.onBeforeLoad);
9450             this.store.un("load", this.onLoad);
9451             this.store.un("loadexception", this.onLoad);
9452         }
9453         if(store){
9454           
9455             store.on("datachanged", this.refresh, this);
9456             store.on("add", this.onAdd, this);
9457             store.on("remove", this.onRemove, this);
9458             store.on("update", this.onUpdate, this);
9459             store.on("clear", this.refresh, this);
9460             store.on("beforeload", this.onBeforeLoad, this);
9461             store.on("load", this.onLoad, this);
9462             store.on("loadexception", this.onLoad, this);
9463         }
9464         
9465         if(store){
9466             this.refresh();
9467         }
9468     },
9469     /**
9470      * onbeforeLoad - masks the loading area.
9471      *
9472      */
9473     onBeforeLoad : function(store,opts)
9474     {
9475          Roo.log('onBeforeLoad');   
9476         if (!opts.add) {
9477             this.el.update("");
9478         }
9479         this.el.mask(this.mask ? this.mask : "Loading" ); 
9480     },
9481     onLoad : function ()
9482     {
9483         this.el.unmask();
9484     },
9485     
9486
9487     /**
9488      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9489      * @param {HTMLElement} node
9490      * @return {HTMLElement} The template node
9491      */
9492     findItemFromChild : function(node){
9493         var el = this.dataName  ?
9494             this.el.child('.roo-tpl-' + this.dataName,true) :
9495             this.el.dom; 
9496         
9497         if(!node || node.parentNode == el){
9498                     return node;
9499             }
9500             var p = node.parentNode;
9501             while(p && p != el){
9502             if(p.parentNode == el){
9503                 return p;
9504             }
9505             p = p.parentNode;
9506         }
9507             return null;
9508     },
9509
9510     /** @ignore */
9511     onClick : function(e){
9512         var item = this.findItemFromChild(e.getTarget());
9513         if(item){
9514             var index = this.indexOf(item);
9515             if(this.onItemClick(item, index, e) !== false){
9516                 this.fireEvent("click", this, index, item, e);
9517             }
9518         }else{
9519             this.clearSelections();
9520         }
9521     },
9522
9523     /** @ignore */
9524     onContextMenu : function(e){
9525         var item = this.findItemFromChild(e.getTarget());
9526         if(item){
9527             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9528         }
9529     },
9530
9531     /** @ignore */
9532     onDblClick : function(e){
9533         var item = this.findItemFromChild(e.getTarget());
9534         if(item){
9535             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9536         }
9537     },
9538
9539     onItemClick : function(item, index, e)
9540     {
9541         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9542             return false;
9543         }
9544         if (this.toggleSelect) {
9545             var m = this.isSelected(item) ? 'unselect' : 'select';
9546             Roo.log(m);
9547             var _t = this;
9548             _t[m](item, true, false);
9549             return true;
9550         }
9551         if(this.multiSelect || this.singleSelect){
9552             if(this.multiSelect && e.shiftKey && this.lastSelection){
9553                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9554             }else{
9555                 this.select(item, this.multiSelect && e.ctrlKey);
9556                 this.lastSelection = item;
9557             }
9558             e.preventDefault();
9559         }
9560         return true;
9561     },
9562
9563     /**
9564      * Get the number of selected nodes.
9565      * @return {Number}
9566      */
9567     getSelectionCount : function(){
9568         return this.selections.length;
9569     },
9570
9571     /**
9572      * Get the currently selected nodes.
9573      * @return {Array} An array of HTMLElements
9574      */
9575     getSelectedNodes : function(){
9576         return this.selections;
9577     },
9578
9579     /**
9580      * Get the indexes of the selected nodes.
9581      * @return {Array}
9582      */
9583     getSelectedIndexes : function(){
9584         var indexes = [], s = this.selections;
9585         for(var i = 0, len = s.length; i < len; i++){
9586             indexes.push(s[i].nodeIndex);
9587         }
9588         return indexes;
9589     },
9590
9591     /**
9592      * Clear all selections
9593      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9594      */
9595     clearSelections : function(suppressEvent){
9596         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9597             this.cmp.elements = this.selections;
9598             this.cmp.removeClass(this.selectedClass);
9599             this.selections = [];
9600             if(!suppressEvent){
9601                 this.fireEvent("selectionchange", this, this.selections);
9602             }
9603         }
9604     },
9605
9606     /**
9607      * Returns true if the passed node is selected
9608      * @param {HTMLElement/Number} node The node or node index
9609      * @return {Boolean}
9610      */
9611     isSelected : function(node){
9612         var s = this.selections;
9613         if(s.length < 1){
9614             return false;
9615         }
9616         node = this.getNode(node);
9617         return s.indexOf(node) !== -1;
9618     },
9619
9620     /**
9621      * Selects nodes.
9622      * @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
9623      * @param {Boolean} keepExisting (optional) true to keep existing selections
9624      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9625      */
9626     select : function(nodeInfo, keepExisting, suppressEvent){
9627         if(nodeInfo instanceof Array){
9628             if(!keepExisting){
9629                 this.clearSelections(true);
9630             }
9631             for(var i = 0, len = nodeInfo.length; i < len; i++){
9632                 this.select(nodeInfo[i], true, true);
9633             }
9634             return;
9635         } 
9636         var node = this.getNode(nodeInfo);
9637         if(!node || this.isSelected(node)){
9638             return; // already selected.
9639         }
9640         if(!keepExisting){
9641             this.clearSelections(true);
9642         }
9643         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9644             Roo.fly(node).addClass(this.selectedClass);
9645             this.selections.push(node);
9646             if(!suppressEvent){
9647                 this.fireEvent("selectionchange", this, this.selections);
9648             }
9649         }
9650         
9651         
9652     },
9653       /**
9654      * Unselects nodes.
9655      * @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
9656      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9657      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9658      */
9659     unselect : function(nodeInfo, keepExisting, suppressEvent)
9660     {
9661         if(nodeInfo instanceof Array){
9662             Roo.each(this.selections, function(s) {
9663                 this.unselect(s, nodeInfo);
9664             }, this);
9665             return;
9666         }
9667         var node = this.getNode(nodeInfo);
9668         if(!node || !this.isSelected(node)){
9669             Roo.log("not selected");
9670             return; // not selected.
9671         }
9672         // fireevent???
9673         var ns = [];
9674         Roo.each(this.selections, function(s) {
9675             if (s == node ) {
9676                 Roo.fly(node).removeClass(this.selectedClass);
9677
9678                 return;
9679             }
9680             ns.push(s);
9681         },this);
9682         
9683         this.selections= ns;
9684         this.fireEvent("selectionchange", this, this.selections);
9685     },
9686
9687     /**
9688      * Gets a template node.
9689      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9690      * @return {HTMLElement} The node or null if it wasn't found
9691      */
9692     getNode : function(nodeInfo){
9693         if(typeof nodeInfo == "string"){
9694             return document.getElementById(nodeInfo);
9695         }else if(typeof nodeInfo == "number"){
9696             return this.nodes[nodeInfo];
9697         }
9698         return nodeInfo;
9699     },
9700
9701     /**
9702      * Gets a range template nodes.
9703      * @param {Number} startIndex
9704      * @param {Number} endIndex
9705      * @return {Array} An array of nodes
9706      */
9707     getNodes : function(start, end){
9708         var ns = this.nodes;
9709         start = start || 0;
9710         end = typeof end == "undefined" ? ns.length - 1 : end;
9711         var nodes = [];
9712         if(start <= end){
9713             for(var i = start; i <= end; i++){
9714                 nodes.push(ns[i]);
9715             }
9716         } else{
9717             for(var i = start; i >= end; i--){
9718                 nodes.push(ns[i]);
9719             }
9720         }
9721         return nodes;
9722     },
9723
9724     /**
9725      * Finds the index of the passed node
9726      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9727      * @return {Number} The index of the node or -1
9728      */
9729     indexOf : function(node){
9730         node = this.getNode(node);
9731         if(typeof node.nodeIndex == "number"){
9732             return node.nodeIndex;
9733         }
9734         var ns = this.nodes;
9735         for(var i = 0, len = ns.length; i < len; i++){
9736             if(ns[i] == node){
9737                 return i;
9738             }
9739         }
9740         return -1;
9741     }
9742 });
9743 /*
9744  * - LGPL
9745  *
9746  * based on jquery fullcalendar
9747  * 
9748  */
9749
9750 Roo.bootstrap = Roo.bootstrap || {};
9751 /**
9752  * @class Roo.bootstrap.Calendar
9753  * @extends Roo.bootstrap.Component
9754  * Bootstrap Calendar class
9755  * @cfg {Boolean} loadMask (true|false) default false
9756     
9757  * @constructor
9758  * Create a new Container
9759  * @param {Object} config The config object
9760  */
9761
9762
9763
9764 Roo.bootstrap.Calendar = function(config){
9765     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9766      this.addEvents({
9767         /**
9768              * @event select
9769              * Fires when a date is selected
9770              * @param {DatePicker} this
9771              * @param {Date} date The selected date
9772              */
9773         'select': true,
9774         /**
9775              * @event monthchange
9776              * Fires when the displayed month changes 
9777              * @param {DatePicker} this
9778              * @param {Date} date The selected month
9779              */
9780         'monthchange': true,
9781         /**
9782              * @event evententer
9783              * Fires when mouse over an event
9784              * @param {Calendar} this
9785              * @param {event} Event
9786              */
9787         'evententer': true,
9788         /**
9789              * @event eventleave
9790              * Fires when the mouse leaves an
9791              * @param {Calendar} this
9792              * @param {event}
9793              */
9794         'eventleave': true,
9795         /**
9796              * @event eventclick
9797              * Fires when the mouse click an
9798              * @param {Calendar} this
9799              * @param {event}
9800              */
9801         'eventclick': true
9802         
9803     });
9804
9805 };
9806
9807 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9808     
9809      /**
9810      * @cfg {Number} startDay
9811      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9812      */
9813     startDay : 0,
9814     
9815     loadMask : false,
9816       
9817     getAutoCreate : function(){
9818         
9819         
9820         var fc_button = function(name, corner, style, content ) {
9821             return Roo.apply({},{
9822                 tag : 'span',
9823                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9824                          (corner.length ?
9825                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9826                             ''
9827                         ),
9828                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9829                 unselectable: 'on'
9830             });
9831         };
9832         
9833         var header = {
9834             tag : 'table',
9835             cls : 'fc-header',
9836             style : 'width:100%',
9837             cn : [
9838                 {
9839                     tag: 'tr',
9840                     cn : [
9841                         {
9842                             tag : 'td',
9843                             cls : 'fc-header-left',
9844                             cn : [
9845                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9846                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9847                                 { tag: 'span', cls: 'fc-header-space' },
9848                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9849                                 
9850                                 
9851                             ]
9852                         },
9853                         
9854                         {
9855                             tag : 'td',
9856                             cls : 'fc-header-center',
9857                             cn : [
9858                                 {
9859                                     tag: 'span',
9860                                     cls: 'fc-header-title',
9861                                     cn : {
9862                                         tag: 'H2',
9863                                         html : 'month / year'
9864                                     }
9865                                 }
9866                                 
9867                             ]
9868                         },
9869                         {
9870                             tag : 'td',
9871                             cls : 'fc-header-right',
9872                             cn : [
9873                           /*      fc_button('month', 'left', '', 'month' ),
9874                                 fc_button('week', '', '', 'week' ),
9875                                 fc_button('day', 'right', '', 'day' )
9876                             */    
9877                                 
9878                             ]
9879                         }
9880                         
9881                     ]
9882                 }
9883             ]
9884         };
9885         
9886        
9887         var cal_heads = function() {
9888             var ret = [];
9889             // fixme - handle this.
9890             
9891             for (var i =0; i < Date.dayNames.length; i++) {
9892                 var d = Date.dayNames[i];
9893                 ret.push({
9894                     tag: 'th',
9895                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9896                     html : d.substring(0,3)
9897                 });
9898                 
9899             }
9900             ret[0].cls += ' fc-first';
9901             ret[6].cls += ' fc-last';
9902             return ret;
9903         };
9904         var cal_cell = function(n) {
9905             return  {
9906                 tag: 'td',
9907                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9908                 cn : [
9909                     {
9910                         cn : [
9911                             {
9912                                 cls: 'fc-day-number',
9913                                 html: 'D'
9914                             },
9915                             {
9916                                 cls: 'fc-day-content',
9917                              
9918                                 cn : [
9919                                      {
9920                                         style: 'position: relative;' // height: 17px;
9921                                     }
9922                                 ]
9923                             }
9924                             
9925                             
9926                         ]
9927                     }
9928                 ]
9929                 
9930             }
9931         };
9932         var cal_rows = function() {
9933             
9934             var ret = []
9935             for (var r = 0; r < 6; r++) {
9936                 var row= {
9937                     tag : 'tr',
9938                     cls : 'fc-week',
9939                     cn : []
9940                 };
9941                 
9942                 for (var i =0; i < Date.dayNames.length; i++) {
9943                     var d = Date.dayNames[i];
9944                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9945
9946                 }
9947                 row.cn[0].cls+=' fc-first';
9948                 row.cn[0].cn[0].style = 'min-height:90px';
9949                 row.cn[6].cls+=' fc-last';
9950                 ret.push(row);
9951                 
9952             }
9953             ret[0].cls += ' fc-first';
9954             ret[4].cls += ' fc-prev-last';
9955             ret[5].cls += ' fc-last';
9956             return ret;
9957             
9958         };
9959         
9960         var cal_table = {
9961             tag: 'table',
9962             cls: 'fc-border-separate',
9963             style : 'width:100%',
9964             cellspacing  : 0,
9965             cn : [
9966                 { 
9967                     tag: 'thead',
9968                     cn : [
9969                         { 
9970                             tag: 'tr',
9971                             cls : 'fc-first fc-last',
9972                             cn : cal_heads()
9973                         }
9974                     ]
9975                 },
9976                 { 
9977                     tag: 'tbody',
9978                     cn : cal_rows()
9979                 }
9980                   
9981             ]
9982         };
9983          
9984          var cfg = {
9985             cls : 'fc fc-ltr',
9986             cn : [
9987                 header,
9988                 {
9989                     cls : 'fc-content',
9990                     style : "position: relative;",
9991                     cn : [
9992                         {
9993                             cls : 'fc-view fc-view-month fc-grid',
9994                             style : 'position: relative',
9995                             unselectable : 'on',
9996                             cn : [
9997                                 {
9998                                     cls : 'fc-event-container',
9999                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10000                                 },
10001                                 cal_table
10002                             ]
10003                         }
10004                     ]
10005     
10006                 }
10007            ] 
10008             
10009         };
10010         
10011          
10012         
10013         return cfg;
10014     },
10015     
10016     
10017     initEvents : function()
10018     {
10019         if(!this.store){
10020             throw "can not find store for calendar";
10021         }
10022         
10023         var mark = {
10024             tag: "div",
10025             cls:"x-dlg-mask",
10026             style: "text-align:center",
10027             cn: [
10028                 {
10029                     tag: "div",
10030                     style: "background-color:white;width:50%;margin:250 auto",
10031                     cn: [
10032                         {
10033                             tag: "img",
10034                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10035                         },
10036                         {
10037                             tag: "span",
10038                             html: "Loading"
10039                         }
10040                         
10041                     ]
10042                 }
10043             ]
10044         }
10045         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10046         
10047         var size = this.el.select('.fc-content', true).first().getSize();
10048         this.maskEl.setSize(size.width, size.height);
10049         this.maskEl.enableDisplayMode("block");
10050         if(!this.loadMask){
10051             this.maskEl.hide();
10052         }
10053         
10054         this.store = Roo.factory(this.store, Roo.data);
10055         this.store.on('load', this.onLoad, this);
10056         this.store.on('beforeload', this.onBeforeLoad, this);
10057         
10058         this.resize();
10059         
10060         this.cells = this.el.select('.fc-day',true);
10061         //Roo.log(this.cells);
10062         this.textNodes = this.el.query('.fc-day-number');
10063         this.cells.addClassOnOver('fc-state-hover');
10064         
10065         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10066         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10067         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10068         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10069         
10070         this.on('monthchange', this.onMonthChange, this);
10071         
10072         this.update(new Date().clearTime());
10073     },
10074     
10075     resize : function() {
10076         var sz  = this.el.getSize();
10077         
10078         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10079         this.el.select('.fc-day-content div',true).setHeight(34);
10080     },
10081     
10082     
10083     // private
10084     showPrevMonth : function(e){
10085         this.update(this.activeDate.add("mo", -1));
10086     },
10087     showToday : function(e){
10088         this.update(new Date().clearTime());
10089     },
10090     // private
10091     showNextMonth : function(e){
10092         this.update(this.activeDate.add("mo", 1));
10093     },
10094
10095     // private
10096     showPrevYear : function(){
10097         this.update(this.activeDate.add("y", -1));
10098     },
10099
10100     // private
10101     showNextYear : function(){
10102         this.update(this.activeDate.add("y", 1));
10103     },
10104
10105     
10106    // private
10107     update : function(date)
10108     {
10109         var vd = this.activeDate;
10110         this.activeDate = date;
10111 //        if(vd && this.el){
10112 //            var t = date.getTime();
10113 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10114 //                Roo.log('using add remove');
10115 //                
10116 //                this.fireEvent('monthchange', this, date);
10117 //                
10118 //                this.cells.removeClass("fc-state-highlight");
10119 //                this.cells.each(function(c){
10120 //                   if(c.dateValue == t){
10121 //                       c.addClass("fc-state-highlight");
10122 //                       setTimeout(function(){
10123 //                            try{c.dom.firstChild.focus();}catch(e){}
10124 //                       }, 50);
10125 //                       return false;
10126 //                   }
10127 //                   return true;
10128 //                });
10129 //                return;
10130 //            }
10131 //        }
10132         
10133         var days = date.getDaysInMonth();
10134         
10135         var firstOfMonth = date.getFirstDateOfMonth();
10136         var startingPos = firstOfMonth.getDay()-this.startDay;
10137         
10138         if(startingPos < this.startDay){
10139             startingPos += 7;
10140         }
10141         
10142         var pm = date.add(Date.MONTH, -1);
10143         var prevStart = pm.getDaysInMonth()-startingPos;
10144 //        
10145         this.cells = this.el.select('.fc-day',true);
10146         this.textNodes = this.el.query('.fc-day-number');
10147         this.cells.addClassOnOver('fc-state-hover');
10148         
10149         var cells = this.cells.elements;
10150         var textEls = this.textNodes;
10151         
10152         Roo.each(cells, function(cell){
10153             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10154         });
10155         
10156         days += startingPos;
10157
10158         // convert everything to numbers so it's fast
10159         var day = 86400000;
10160         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10161         //Roo.log(d);
10162         //Roo.log(pm);
10163         //Roo.log(prevStart);
10164         
10165         var today = new Date().clearTime().getTime();
10166         var sel = date.clearTime().getTime();
10167         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10168         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10169         var ddMatch = this.disabledDatesRE;
10170         var ddText = this.disabledDatesText;
10171         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10172         var ddaysText = this.disabledDaysText;
10173         var format = this.format;
10174         
10175         var setCellClass = function(cal, cell){
10176             
10177             //Roo.log('set Cell Class');
10178             cell.title = "";
10179             var t = d.getTime();
10180             
10181             //Roo.log(d);
10182             
10183             cell.dateValue = t;
10184             if(t == today){
10185                 cell.className += " fc-today";
10186                 cell.className += " fc-state-highlight";
10187                 cell.title = cal.todayText;
10188             }
10189             if(t == sel){
10190                 // disable highlight in other month..
10191                 //cell.className += " fc-state-highlight";
10192                 
10193             }
10194             // disabling
10195             if(t < min) {
10196                 cell.className = " fc-state-disabled";
10197                 cell.title = cal.minText;
10198                 return;
10199             }
10200             if(t > max) {
10201                 cell.className = " fc-state-disabled";
10202                 cell.title = cal.maxText;
10203                 return;
10204             }
10205             if(ddays){
10206                 if(ddays.indexOf(d.getDay()) != -1){
10207                     cell.title = ddaysText;
10208                     cell.className = " fc-state-disabled";
10209                 }
10210             }
10211             if(ddMatch && format){
10212                 var fvalue = d.dateFormat(format);
10213                 if(ddMatch.test(fvalue)){
10214                     cell.title = ddText.replace("%0", fvalue);
10215                     cell.className = " fc-state-disabled";
10216                 }
10217             }
10218             
10219             if (!cell.initialClassName) {
10220                 cell.initialClassName = cell.dom.className;
10221             }
10222             
10223             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10224         };
10225
10226         var i = 0;
10227         
10228         for(; i < startingPos; i++) {
10229             textEls[i].innerHTML = (++prevStart);
10230             d.setDate(d.getDate()+1);
10231             
10232             cells[i].className = "fc-past fc-other-month";
10233             setCellClass(this, cells[i]);
10234         }
10235         
10236         var intDay = 0;
10237         
10238         for(; i < days; i++){
10239             intDay = i - startingPos + 1;
10240             textEls[i].innerHTML = (intDay);
10241             d.setDate(d.getDate()+1);
10242             
10243             cells[i].className = ''; // "x-date-active";
10244             setCellClass(this, cells[i]);
10245         }
10246         var extraDays = 0;
10247         
10248         for(; i < 42; i++) {
10249             textEls[i].innerHTML = (++extraDays);
10250             d.setDate(d.getDate()+1);
10251             
10252             cells[i].className = "fc-future fc-other-month";
10253             setCellClass(this, cells[i]);
10254         }
10255         
10256         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10257         
10258         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10259         
10260         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10261         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10262         
10263         if(totalRows != 6){
10264             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10265             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10266         }
10267         
10268         this.fireEvent('monthchange', this, date);
10269         
10270         
10271         /*
10272         if(!this.internalRender){
10273             var main = this.el.dom.firstChild;
10274             var w = main.offsetWidth;
10275             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10276             Roo.fly(main).setWidth(w);
10277             this.internalRender = true;
10278             // opera does not respect the auto grow header center column
10279             // then, after it gets a width opera refuses to recalculate
10280             // without a second pass
10281             if(Roo.isOpera && !this.secondPass){
10282                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10283                 this.secondPass = true;
10284                 this.update.defer(10, this, [date]);
10285             }
10286         }
10287         */
10288         
10289     },
10290     
10291     findCell : function(dt) {
10292         dt = dt.clearTime().getTime();
10293         var ret = false;
10294         this.cells.each(function(c){
10295             //Roo.log("check " +c.dateValue + '?=' + dt);
10296             if(c.dateValue == dt){
10297                 ret = c;
10298                 return false;
10299             }
10300             return true;
10301         });
10302         
10303         return ret;
10304     },
10305     
10306     findCells : function(ev) {
10307         var s = ev.start.clone().clearTime().getTime();
10308        // Roo.log(s);
10309         var e= ev.end.clone().clearTime().getTime();
10310        // Roo.log(e);
10311         var ret = [];
10312         this.cells.each(function(c){
10313              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10314             
10315             if(c.dateValue > e){
10316                 return ;
10317             }
10318             if(c.dateValue < s){
10319                 return ;
10320             }
10321             ret.push(c);
10322         });
10323         
10324         return ret;    
10325     },
10326     
10327     findBestRow: function(cells)
10328     {
10329         var ret = 0;
10330         
10331         for (var i =0 ; i < cells.length;i++) {
10332             ret  = Math.max(cells[i].rows || 0,ret);
10333         }
10334         return ret;
10335         
10336     },
10337     
10338     
10339     addItem : function(ev)
10340     {
10341         // look for vertical location slot in
10342         var cells = this.findCells(ev);
10343         
10344         ev.row = this.findBestRow(cells);
10345         
10346         // work out the location.
10347         
10348         var crow = false;
10349         var rows = [];
10350         for(var i =0; i < cells.length; i++) {
10351             if (!crow) {
10352                 crow = {
10353                     start : cells[i],
10354                     end :  cells[i]
10355                 };
10356                 continue;
10357             }
10358             if (crow.start.getY() == cells[i].getY()) {
10359                 // on same row.
10360                 crow.end = cells[i];
10361                 continue;
10362             }
10363             // different row.
10364             rows.push(crow);
10365             crow = {
10366                 start: cells[i],
10367                 end : cells[i]
10368             };
10369             
10370         }
10371         
10372         rows.push(crow);
10373         ev.els = [];
10374         ev.rows = rows;
10375         ev.cells = cells;
10376         for (var i = 0; i < cells.length;i++) {
10377             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10378             
10379         }
10380         
10381         this.calevents.push(ev);
10382     },
10383     
10384     clearEvents: function() {
10385         
10386         if(!this.calevents){
10387             return;
10388         }
10389         
10390         Roo.each(this.cells.elements, function(c){
10391             c.rows = 0;
10392         });
10393         
10394         Roo.each(this.calevents, function(e) {
10395             Roo.each(e.els, function(el) {
10396                 el.un('mouseenter' ,this.onEventEnter, this);
10397                 el.un('mouseleave' ,this.onEventLeave, this);
10398                 el.remove();
10399             },this);
10400         },this);
10401         
10402     },
10403     
10404     renderEvents: function()
10405     {   
10406         // first make sure there is enough space..
10407         
10408         this.cells.each(function(c) {
10409         
10410             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10411         });
10412         
10413         for (var e = 0; e < this.calevents.length; e++) {
10414             var ev = this.calevents[e];
10415             var cells = ev.cells;
10416             var rows = ev.rows;
10417             
10418             for(var i =0; i < rows.length; i++) {
10419                 
10420                  
10421                 // how many rows should it span..
10422                 
10423                 var  cfg = {
10424                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10425                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10426                     
10427                     unselectable : "on",
10428                     cn : [
10429                         {
10430                             cls: 'fc-event-inner',
10431                             cn : [
10432 //                                {
10433 //                                  tag:'span',
10434 //                                  cls: 'fc-event-time',
10435 //                                  html : cells.length > 1 ? '' : ev.time
10436 //                                },
10437                                 {
10438                                   tag:'span',
10439                                   cls: 'fc-event-title',
10440                                   html : String.format('{0}', ev.title)
10441                                 }
10442                                 
10443                                 
10444                             ]
10445                         },
10446                         {
10447                             cls: 'ui-resizable-handle ui-resizable-e',
10448                             html : '&nbsp;&nbsp;&nbsp'
10449                         }
10450                         
10451                     ]
10452                 };
10453                 if (i == 0) {
10454                     cfg.cls += ' fc-event-start';
10455                 }
10456                 if ((i+1) == rows.length) {
10457                     cfg.cls += ' fc-event-end';
10458                 }
10459                 
10460                 var ctr = this.el.select('.fc-event-container',true).first();
10461                 var cg = ctr.createChild(cfg);
10462                 
10463                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10464                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10465                 cg.on('click', this.onEventClick, this, ev);
10466                 
10467                 ev.els.push(cg);
10468                 
10469                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10470                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10471                 //Roo.log(cg);
10472                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10473                 cg.setWidth(ebox.right - sbox.x -2);
10474             }
10475             
10476             
10477         }
10478         
10479     },
10480     
10481     onEventEnter: function (e, el,event,d) {
10482         this.fireEvent('evententer', this, el, event);
10483     },
10484     
10485     onEventLeave: function (e, el,event,d) {
10486         this.fireEvent('eventleave', this, el, event);
10487     },
10488     
10489     onEventClick: function (e, el,event,d) {
10490         this.fireEvent('eventclick', this, el, event);
10491     },
10492     
10493     onMonthChange: function () {
10494         this.store.load();
10495     },
10496     
10497     onLoad: function () 
10498     {   
10499         this.calevents = [];
10500         var cal = this;
10501         
10502         if(this.store.getCount() > 0){
10503             this.store.data.each(function(d){
10504                cal.addItem({
10505                     id : d.data.id,
10506                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10507                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10508                     time : d.data.start_time,
10509                     title : d.data.title,
10510                     description : d.data.description,
10511                     venue : d.data.venue
10512                 });
10513             });
10514         }
10515         
10516         this.renderEvents();
10517         
10518         if(this.loadMask){
10519             this.maskEl.hide();
10520         }
10521     },
10522     
10523     onBeforeLoad: function()
10524     {
10525         this.clearEvents();
10526         
10527         if(this.loadMask){
10528             this.maskEl.show();
10529         }
10530     }
10531 });
10532
10533  
10534  /*
10535  * - LGPL
10536  *
10537  * element
10538  * 
10539  */
10540
10541 /**
10542  * @class Roo.bootstrap.Popover
10543  * @extends Roo.bootstrap.Component
10544  * Bootstrap Popover class
10545  * @cfg {String} html contents of the popover   (or false to use children..)
10546  * @cfg {String} title of popover (or false to hide)
10547  * @cfg {String} placement how it is placed
10548  * @cfg {String} trigger click || hover (or false to trigger manually)
10549  * @cfg {String} over what (parent or false to trigger manually.)
10550  * 
10551  * @constructor
10552  * Create a new Popover
10553  * @param {Object} config The config object
10554  */
10555
10556 Roo.bootstrap.Popover = function(config){
10557     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10558 };
10559
10560 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10561     
10562     title: 'Fill in a title',
10563     html: false,
10564     
10565     placement : 'right',
10566     trigger : 'hover', // hover
10567     
10568     over: 'parent',
10569     
10570     can_build_overlaid : false,
10571     
10572     getChildContainer : function()
10573     {
10574         return this.el.select('.popover-content',true).first();
10575     },
10576     
10577     getAutoCreate : function(){
10578          Roo.log('make popover?');
10579         var cfg = {
10580            cls : 'popover roo-dynamic',
10581            style: 'display:block',
10582            cn : [
10583                 {
10584                     cls : 'arrow'
10585                 },
10586                 {
10587                     cls : 'popover-inner',
10588                     cn : [
10589                         {
10590                             tag: 'h3',
10591                             cls: 'popover-title',
10592                             html : this.title
10593                         },
10594                         {
10595                             cls : 'popover-content',
10596                             html : this.html
10597                         }
10598                     ]
10599                     
10600                 }
10601            ]
10602         };
10603         
10604         return cfg;
10605     },
10606     setTitle: function(str)
10607     {
10608         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10609     },
10610     setContent: function(str)
10611     {
10612         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10613     },
10614     // as it get's added to the bottom of the page.
10615     onRender : function(ct, position)
10616     {
10617         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10618         if(!this.el){
10619             var cfg = Roo.apply({},  this.getAutoCreate());
10620             cfg.id = Roo.id();
10621             
10622             if (this.cls) {
10623                 cfg.cls += ' ' + this.cls;
10624             }
10625             if (this.style) {
10626                 cfg.style = this.style;
10627             }
10628             Roo.log("adding to ")
10629             this.el = Roo.get(document.body).createChild(cfg, position);
10630             Roo.log(this.el);
10631         }
10632         this.initEvents();
10633     },
10634     
10635     initEvents : function()
10636     {
10637         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10638         this.el.enableDisplayMode('block');
10639         this.el.hide();
10640         if (this.over === false) {
10641             return; 
10642         }
10643         if (this.triggers === false) {
10644             return;
10645         }
10646         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10647         var triggers = this.trigger ? this.trigger.split(' ') : [];
10648         Roo.each(triggers, function(trigger) {
10649         
10650             if (trigger == 'click') {
10651                 on_el.on('click', this.toggle, this);
10652             } else if (trigger != 'manual') {
10653                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10654                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10655       
10656                 on_el.on(eventIn  ,this.enter, this);
10657                 on_el.on(eventOut, this.leave, this);
10658             }
10659         }, this);
10660         
10661     },
10662     
10663     
10664     // private
10665     timeout : null,
10666     hoverState : null,
10667     
10668     toggle : function () {
10669         this.hoverState == 'in' ? this.leave() : this.enter();
10670     },
10671     
10672     enter : function () {
10673        
10674     
10675         clearTimeout(this.timeout);
10676     
10677         this.hoverState = 'in'
10678     
10679         if (!this.delay || !this.delay.show) {
10680             this.show();
10681             return 
10682         }
10683         var _t = this;
10684         this.timeout = setTimeout(function () {
10685             if (_t.hoverState == 'in') {
10686                 _t.show();
10687             }
10688         }, this.delay.show)
10689     },
10690     leave : function() {
10691         clearTimeout(this.timeout);
10692     
10693         this.hoverState = 'out'
10694     
10695         if (!this.delay || !this.delay.hide) {
10696             this.hide();
10697             return 
10698         }
10699         var _t = this;
10700         this.timeout = setTimeout(function () {
10701             if (_t.hoverState == 'out') {
10702                 _t.hide();
10703             }
10704         }, this.delay.hide)
10705     },
10706     
10707     show : function (on_el)
10708     {
10709         if (!on_el) {
10710             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10711         }
10712         // set content.
10713         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10714         if (this.html !== false) {
10715             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10716         }
10717         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10718         if (!this.title.length) {
10719             this.el.select('.popover-title',true).hide();
10720         }
10721         
10722         var placement = typeof this.placement == 'function' ?
10723             this.placement.call(this, this.el, on_el) :
10724             this.placement;
10725             
10726         var autoToken = /\s?auto?\s?/i;
10727         var autoPlace = autoToken.test(placement);
10728         if (autoPlace) {
10729             placement = placement.replace(autoToken, '') || 'top';
10730         }
10731         
10732         //this.el.detach()
10733         //this.el.setXY([0,0]);
10734         this.el.show();
10735         this.el.dom.style.display='block';
10736         this.el.addClass(placement);
10737         
10738         //this.el.appendTo(on_el);
10739         
10740         var p = this.getPosition();
10741         var box = this.el.getBox();
10742         
10743         if (autoPlace) {
10744             // fixme..
10745         }
10746         var align = Roo.bootstrap.Popover.alignment[placement]
10747         this.el.alignTo(on_el, align[0],align[1]);
10748         //var arrow = this.el.select('.arrow',true).first();
10749         //arrow.set(align[2], 
10750         
10751         this.el.addClass('in');
10752         this.hoverState = null;
10753         
10754         if (this.el.hasClass('fade')) {
10755             // fade it?
10756         }
10757         
10758     },
10759     hide : function()
10760     {
10761         this.el.setXY([0,0]);
10762         this.el.removeClass('in');
10763         this.el.hide();
10764         
10765     }
10766     
10767 });
10768
10769 Roo.bootstrap.Popover.alignment = {
10770     'left' : ['r-l', [-10,0], 'right'],
10771     'right' : ['l-r', [10,0], 'left'],
10772     'bottom' : ['t-b', [0,10], 'top'],
10773     'top' : [ 'b-t', [0,-10], 'bottom']
10774 };
10775
10776  /*
10777  * - LGPL
10778  *
10779  * Progress
10780  * 
10781  */
10782
10783 /**
10784  * @class Roo.bootstrap.Progress
10785  * @extends Roo.bootstrap.Component
10786  * Bootstrap Progress class
10787  * @cfg {Boolean} striped striped of the progress bar
10788  * @cfg {Boolean} active animated of the progress bar
10789  * 
10790  * 
10791  * @constructor
10792  * Create a new Progress
10793  * @param {Object} config The config object
10794  */
10795
10796 Roo.bootstrap.Progress = function(config){
10797     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10798 };
10799
10800 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10801     
10802     striped : false,
10803     active: false,
10804     
10805     getAutoCreate : function(){
10806         var cfg = {
10807             tag: 'div',
10808             cls: 'progress'
10809         };
10810         
10811         
10812         if(this.striped){
10813             cfg.cls += ' progress-striped';
10814         }
10815       
10816         if(this.active){
10817             cfg.cls += ' active';
10818         }
10819         
10820         
10821         return cfg;
10822     }
10823    
10824 });
10825
10826  
10827
10828  /*
10829  * - LGPL
10830  *
10831  * ProgressBar
10832  * 
10833  */
10834
10835 /**
10836  * @class Roo.bootstrap.ProgressBar
10837  * @extends Roo.bootstrap.Component
10838  * Bootstrap ProgressBar class
10839  * @cfg {Number} aria_valuenow aria-value now
10840  * @cfg {Number} aria_valuemin aria-value min
10841  * @cfg {Number} aria_valuemax aria-value max
10842  * @cfg {String} label label for the progress bar
10843  * @cfg {String} panel (success | info | warning | danger )
10844  * @cfg {String} role role of the progress bar
10845  * @cfg {String} sr_only text
10846  * 
10847  * 
10848  * @constructor
10849  * Create a new ProgressBar
10850  * @param {Object} config The config object
10851  */
10852
10853 Roo.bootstrap.ProgressBar = function(config){
10854     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10855 };
10856
10857 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10858     
10859     aria_valuenow : 0,
10860     aria_valuemin : 0,
10861     aria_valuemax : 100,
10862     label : false,
10863     panel : false,
10864     role : false,
10865     sr_only: false,
10866     
10867     getAutoCreate : function()
10868     {
10869         
10870         var cfg = {
10871             tag: 'div',
10872             cls: 'progress-bar',
10873             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10874         };
10875         
10876         if(this.sr_only){
10877             cfg.cn = {
10878                 tag: 'span',
10879                 cls: 'sr-only',
10880                 html: this.sr_only
10881             }
10882         }
10883         
10884         if(this.role){
10885             cfg.role = this.role;
10886         }
10887         
10888         if(this.aria_valuenow){
10889             cfg['aria-valuenow'] = this.aria_valuenow;
10890         }
10891         
10892         if(this.aria_valuemin){
10893             cfg['aria-valuemin'] = this.aria_valuemin;
10894         }
10895         
10896         if(this.aria_valuemax){
10897             cfg['aria-valuemax'] = this.aria_valuemax;
10898         }
10899         
10900         if(this.label && !this.sr_only){
10901             cfg.html = this.label;
10902         }
10903         
10904         if(this.panel){
10905             cfg.cls += ' progress-bar-' + this.panel;
10906         }
10907         
10908         return cfg;
10909     },
10910     
10911     update : function(aria_valuenow)
10912     {
10913         this.aria_valuenow = aria_valuenow;
10914         
10915         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10916     }
10917    
10918 });
10919
10920  
10921
10922  /*
10923  * - LGPL
10924  *
10925  * TabPanel
10926  * 
10927  */
10928
10929 /**
10930  * @class Roo.bootstrap.TabPanel
10931  * @extends Roo.bootstrap.Component
10932  * Bootstrap TabPanel class
10933  * @cfg {Boolean} active panel active
10934  * @cfg {String} html panel content
10935  * @cfg {String} tabId tab relate id
10936  * 
10937  * 
10938  * @constructor
10939  * Create a new TabPanel
10940  * @param {Object} config The config object
10941  */
10942
10943 Roo.bootstrap.TabPanel = function(config){
10944     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10945 };
10946
10947 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10948     
10949     active: false,
10950     html: false,
10951     tabId: false,
10952     
10953     getAutoCreate : function(){
10954         var cfg = {
10955             tag: 'div',
10956             cls: 'tab-pane',
10957             html: this.html || ''
10958         };
10959         
10960         if(this.active){
10961             cfg.cls += ' active';
10962         }
10963         
10964         if(this.tabId){
10965             cfg.tabId = this.tabId;
10966         }
10967         
10968         return cfg;
10969     }
10970    
10971 });
10972
10973  
10974
10975  /*
10976  * - LGPL
10977  *
10978  * DateField
10979  * 
10980  */
10981
10982 /**
10983  * @class Roo.bootstrap.DateField
10984  * @extends Roo.bootstrap.Input
10985  * Bootstrap DateField class
10986  * @cfg {Number} weekStart default 0
10987  * @cfg {Number} weekStart default 0
10988  * @cfg {Number} viewMode default empty, (months|years)
10989  * @cfg {Number} minViewMode default empty, (months|years)
10990  * @cfg {Number} startDate default -Infinity
10991  * @cfg {Number} endDate default Infinity
10992  * @cfg {Boolean} todayHighlight default false
10993  * @cfg {Boolean} todayBtn default false
10994  * @cfg {Boolean} calendarWeeks default false
10995  * @cfg {Object} daysOfWeekDisabled default empty
10996  * 
10997  * @cfg {Boolean} keyboardNavigation default true
10998  * @cfg {String} language default en
10999  * 
11000  * @constructor
11001  * Create a new DateField
11002  * @param {Object} config The config object
11003  */
11004
11005 Roo.bootstrap.DateField = function(config){
11006     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11007      this.addEvents({
11008             /**
11009              * @event show
11010              * Fires when this field show.
11011              * @param {Roo.bootstrap.DateField} this
11012              * @param {Mixed} date The date value
11013              */
11014             show : true,
11015             /**
11016              * @event show
11017              * Fires when this field hide.
11018              * @param {Roo.bootstrap.DateField} this
11019              * @param {Mixed} date The date value
11020              */
11021             hide : true,
11022             /**
11023              * @event select
11024              * Fires when select a date.
11025              * @param {Roo.bootstrap.DateField} this
11026              * @param {Mixed} date The date value
11027              */
11028             select : true
11029         });
11030 };
11031
11032 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11033     
11034     /**
11035      * @cfg {String} format
11036      * The default date format string which can be overriden for localization support.  The format must be
11037      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11038      */
11039     format : "m/d/y",
11040     /**
11041      * @cfg {String} altFormats
11042      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11043      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11044      */
11045     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11046     
11047     weekStart : 0,
11048     
11049     viewMode : '',
11050     
11051     minViewMode : '',
11052     
11053     todayHighlight : false,
11054     
11055     todayBtn: false,
11056     
11057     language: 'en',
11058     
11059     keyboardNavigation: true,
11060     
11061     calendarWeeks: false,
11062     
11063     startDate: -Infinity,
11064     
11065     endDate: Infinity,
11066     
11067     daysOfWeekDisabled: [],
11068     
11069     _events: [],
11070     
11071     UTCDate: function()
11072     {
11073         return new Date(Date.UTC.apply(Date, arguments));
11074     },
11075     
11076     UTCToday: function()
11077     {
11078         var today = new Date();
11079         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11080     },
11081     
11082     getDate: function() {
11083             var d = this.getUTCDate();
11084             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11085     },
11086     
11087     getUTCDate: function() {
11088             return this.date;
11089     },
11090     
11091     setDate: function(d) {
11092             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11093     },
11094     
11095     setUTCDate: function(d) {
11096             this.date = d;
11097             this.setValue(this.formatDate(this.date));
11098     },
11099         
11100     onRender: function(ct, position)
11101     {
11102         
11103         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11104         
11105         this.language = this.language || 'en';
11106         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11107         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11108         
11109         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11110         this.format = this.format || 'm/d/y';
11111         this.isInline = false;
11112         this.isInput = true;
11113         this.component = this.el.select('.add-on', true).first() || false;
11114         this.component = (this.component && this.component.length === 0) ? false : this.component;
11115         this.hasInput = this.component && this.inputEL().length;
11116         
11117         if (typeof(this.minViewMode === 'string')) {
11118             switch (this.minViewMode) {
11119                 case 'months':
11120                     this.minViewMode = 1;
11121                     break;
11122                 case 'years':
11123                     this.minViewMode = 2;
11124                     break;
11125                 default:
11126                     this.minViewMode = 0;
11127                     break;
11128             }
11129         }
11130         
11131         if (typeof(this.viewMode === 'string')) {
11132             switch (this.viewMode) {
11133                 case 'months':
11134                     this.viewMode = 1;
11135                     break;
11136                 case 'years':
11137                     this.viewMode = 2;
11138                     break;
11139                 default:
11140                     this.viewMode = 0;
11141                     break;
11142             }
11143         }
11144                 
11145         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11146         
11147         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11148         
11149         this.picker().on('mousedown', this.onMousedown, this);
11150         this.picker().on('click', this.onClick, this);
11151         
11152         this.picker().addClass('datepicker-dropdown');
11153         
11154         this.startViewMode = this.viewMode;
11155         
11156         
11157         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11158             if(!this.calendarWeeks){
11159                 v.remove();
11160                 return;
11161             };
11162             
11163             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11164             v.attr('colspan', function(i, val){
11165                 return parseInt(val) + 1;
11166             });
11167         })
11168                         
11169         
11170         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11171         
11172         this.setStartDate(this.startDate);
11173         this.setEndDate(this.endDate);
11174         
11175         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11176         
11177         this.fillDow();
11178         this.fillMonths();
11179         this.update();
11180         this.showMode();
11181         
11182         if(this.isInline) {
11183             this.show();
11184         }
11185     },
11186     
11187     picker : function()
11188     {
11189         return this.el.select('.datepicker', true).first();
11190     },
11191     
11192     fillDow: function()
11193     {
11194         var dowCnt = this.weekStart;
11195         
11196         var dow = {
11197             tag: 'tr',
11198             cn: [
11199                 
11200             ]
11201         };
11202         
11203         if(this.calendarWeeks){
11204             dow.cn.push({
11205                 tag: 'th',
11206                 cls: 'cw',
11207                 html: '&nbsp;'
11208             })
11209         }
11210         
11211         while (dowCnt < this.weekStart + 7) {
11212             dow.cn.push({
11213                 tag: 'th',
11214                 cls: 'dow',
11215                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11216             });
11217         }
11218         
11219         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11220     },
11221     
11222     fillMonths: function()
11223     {    
11224         var i = 0
11225         var months = this.picker().select('>.datepicker-months td', true).first();
11226         
11227         months.dom.innerHTML = '';
11228         
11229         while (i < 12) {
11230             var month = {
11231                 tag: 'span',
11232                 cls: 'month',
11233                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11234             }
11235             
11236             months.createChild(month);
11237         }
11238         
11239     },
11240     
11241     update: function(){
11242         
11243         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11244         
11245         if (this.date < this.startDate) {
11246             this.viewDate = new Date(this.startDate);
11247         } else if (this.date > this.endDate) {
11248             this.viewDate = new Date(this.endDate);
11249         } else {
11250             this.viewDate = new Date(this.date);
11251         }
11252         
11253         this.fill();
11254     },
11255     
11256     fill: function() {
11257         var d = new Date(this.viewDate),
11258                 year = d.getUTCFullYear(),
11259                 month = d.getUTCMonth(),
11260                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11261                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11262                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11263                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11264                 currentDate = this.date && this.date.valueOf(),
11265                 today = this.UTCToday();
11266         
11267         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11268         
11269 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11270         
11271 //        this.picker.select('>tfoot th.today').
11272 //                                              .text(dates[this.language].today)
11273 //                                              .toggle(this.todayBtn !== false);
11274     
11275         this.updateNavArrows();
11276         this.fillMonths();
11277                                                 
11278         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11279         
11280         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11281          
11282         prevMonth.setUTCDate(day);
11283         
11284         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11285         
11286         var nextMonth = new Date(prevMonth);
11287         
11288         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11289         
11290         nextMonth = nextMonth.valueOf();
11291         
11292         var fillMonths = false;
11293         
11294         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11295         
11296         while(prevMonth.valueOf() < nextMonth) {
11297             var clsName = '';
11298             
11299             if (prevMonth.getUTCDay() === this.weekStart) {
11300                 if(fillMonths){
11301                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11302                 }
11303                     
11304                 fillMonths = {
11305                     tag: 'tr',
11306                     cn: []
11307                 };
11308                 
11309                 if(this.calendarWeeks){
11310                     // ISO 8601: First week contains first thursday.
11311                     // ISO also states week starts on Monday, but we can be more abstract here.
11312                     var
11313                     // Start of current week: based on weekstart/current date
11314                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11315                     // Thursday of this week
11316                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11317                     // First Thursday of year, year from thursday
11318                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11319                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11320                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11321                     
11322                     fillMonths.cn.push({
11323                         tag: 'td',
11324                         cls: 'cw',
11325                         html: calWeek
11326                     });
11327                 }
11328             }
11329             
11330             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11331                 clsName += ' old';
11332             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11333                 clsName += ' new';
11334             }
11335             if (this.todayHighlight &&
11336                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11337                 prevMonth.getUTCMonth() == today.getMonth() &&
11338                 prevMonth.getUTCDate() == today.getDate()) {
11339                 clsName += ' today';
11340             }
11341             
11342             if (currentDate && prevMonth.valueOf() === currentDate) {
11343                 clsName += ' active';
11344             }
11345             
11346             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11347                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11348                     clsName += ' disabled';
11349             }
11350             
11351             fillMonths.cn.push({
11352                 tag: 'td',
11353                 cls: 'day ' + clsName,
11354                 html: prevMonth.getDate()
11355             })
11356             
11357             prevMonth.setDate(prevMonth.getDate()+1);
11358         }
11359           
11360         var currentYear = this.date && this.date.getUTCFullYear();
11361         var currentMonth = this.date && this.date.getUTCMonth();
11362         
11363         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11364         
11365         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11366             v.removeClass('active');
11367             
11368             if(currentYear === year && k === currentMonth){
11369                 v.addClass('active');
11370             }
11371             
11372             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11373                 v.addClass('disabled');
11374             }
11375             
11376         });
11377         
11378         
11379         year = parseInt(year/10, 10) * 10;
11380         
11381         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11382         
11383         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11384         
11385         year -= 1;
11386         for (var i = -1; i < 11; i++) {
11387             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11388                 tag: 'span',
11389                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11390                 html: year
11391             })
11392             
11393             year += 1;
11394         }
11395     },
11396     
11397     showMode: function(dir) {
11398         if (dir) {
11399             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11400         }
11401         Roo.each(this.picker().select('>div',true).elements, function(v){
11402             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11403             v.hide();
11404         });
11405         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11406     },
11407     
11408     place: function()
11409     {
11410         if(this.isInline) return;
11411         
11412         this.picker().removeClass(['bottom', 'top']);
11413         
11414         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11415             /*
11416              * place to the top of element!
11417              *
11418              */
11419             
11420             this.picker().addClass('top');
11421             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11422             
11423             return;
11424         }
11425         
11426         this.picker().addClass('bottom');
11427         
11428         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11429     },
11430     
11431     parseDate : function(value){
11432         if(!value || value instanceof Date){
11433             return value;
11434         }
11435         var v = Date.parseDate(value, this.format);
11436         if (!v && this.useIso) {
11437             v = Date.parseDate(value, 'Y-m-d');
11438         }
11439         if(!v && this.altFormats){
11440             if(!this.altFormatsArray){
11441                 this.altFormatsArray = this.altFormats.split("|");
11442             }
11443             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11444                 v = Date.parseDate(value, this.altFormatsArray[i]);
11445             }
11446         }
11447         return v;
11448     },
11449     
11450     formatDate : function(date, fmt){
11451         return (!date || !(date instanceof Date)) ?
11452         date : date.dateFormat(fmt || this.format);
11453     },
11454     
11455     onFocus : function()
11456     {
11457         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11458         this.show();
11459     },
11460     
11461     onBlur : function()
11462     {
11463         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11464         this.hide();
11465     },
11466     
11467     show : function()
11468     {
11469         this.picker().show();
11470         this.update();
11471         this.place();
11472         
11473         this.fireEvent('show', this, this.date);
11474     },
11475     
11476     hide : function()
11477     {
11478         if(this.isInline) return;
11479         this.picker().hide();
11480         this.viewMode = this.startViewMode;
11481         this.showMode();
11482         
11483         this.fireEvent('hide', this, this.date);
11484         
11485     },
11486     
11487     onMousedown: function(e){
11488         e.stopPropagation();
11489         e.preventDefault();
11490     },
11491     
11492     keyup: function(e){
11493         Roo.bootstrap.DateField.superclass.keyup.call(this);
11494         this.update();
11495         
11496     },
11497
11498     setValue: function(v){
11499         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11500         
11501         this.fireEvent('select', this, this.date);
11502         
11503     },
11504     
11505     fireKey: function(e){
11506         if (!this.picker().isVisible()){
11507             if (e.keyCode == 27) // allow escape to hide and re-show picker
11508                 this.show();
11509             return;
11510         }
11511         var dateChanged = false,
11512         dir, day, month,
11513         newDate, newViewDate;
11514         switch(e.keyCode){
11515             case 27: // escape
11516                 this.hide();
11517                 e.preventDefault();
11518                 break;
11519             case 37: // left
11520             case 39: // right
11521                 if (!this.keyboardNavigation) break;
11522                 dir = e.keyCode == 37 ? -1 : 1;
11523                 
11524                 if (e.ctrlKey){
11525                     newDate = this.moveYear(this.date, dir);
11526                     newViewDate = this.moveYear(this.viewDate, dir);
11527                 } else if (e.shiftKey){
11528                     newDate = this.moveMonth(this.date, dir);
11529                     newViewDate = this.moveMonth(this.viewDate, dir);
11530                 } else {
11531                     newDate = new Date(this.date);
11532                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11533                     newViewDate = new Date(this.viewDate);
11534                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11535                 }
11536                 if (this.dateWithinRange(newDate)){
11537                     this.date = newDate;
11538                     this.viewDate = newViewDate;
11539                     this.setValue(this.formatDate(this.date));
11540                     this.update();
11541                     e.preventDefault();
11542                     dateChanged = true;
11543                 }
11544                 break;
11545             case 38: // up
11546             case 40: // down
11547                 if (!this.keyboardNavigation) break;
11548                 dir = e.keyCode == 38 ? -1 : 1;
11549                 if (e.ctrlKey){
11550                     newDate = this.moveYear(this.date, dir);
11551                     newViewDate = this.moveYear(this.viewDate, dir);
11552                 } else if (e.shiftKey){
11553                     newDate = this.moveMonth(this.date, dir);
11554                     newViewDate = this.moveMonth(this.viewDate, dir);
11555                 } else {
11556                     newDate = new Date(this.date);
11557                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11558                     newViewDate = new Date(this.viewDate);
11559                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11560                 }
11561                 if (this.dateWithinRange(newDate)){
11562                     this.date = newDate;
11563                     this.viewDate = newViewDate;
11564                     this.setValue(this.formatDate(this.date));
11565                     this.update();
11566                     e.preventDefault();
11567                     dateChanged = true;
11568                 }
11569                 break;
11570             case 13: // enter
11571                 this.setValue(this.formatDate(this.date));
11572                 this.hide();
11573                 e.preventDefault();
11574                 break;
11575             case 9: // tab
11576                 this.setValue(this.formatDate(this.date));
11577                 this.hide();
11578                 break;
11579         }
11580     },
11581     
11582     
11583     onClick: function(e) {
11584         e.stopPropagation();
11585         e.preventDefault();
11586         
11587         var target = e.getTarget();
11588         
11589         if(target.nodeName.toLowerCase() === 'i'){
11590             target = Roo.get(target).dom.parentNode;
11591         }
11592         
11593         var nodeName = target.nodeName;
11594         var className = target.className;
11595         var html = target.innerHTML;
11596         
11597         switch(nodeName.toLowerCase()) {
11598             case 'th':
11599                 switch(className) {
11600                     case 'switch':
11601                         this.showMode(1);
11602                         break;
11603                     case 'prev':
11604                     case 'next':
11605                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11606                         switch(this.viewMode){
11607                                 case 0:
11608                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11609                                         break;
11610                                 case 1:
11611                                 case 2:
11612                                         this.viewDate = this.moveYear(this.viewDate, dir);
11613                                         break;
11614                         }
11615                         this.fill();
11616                         break;
11617                     case 'today':
11618                         var date = new Date();
11619                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11620                         this.fill()
11621                         this.setValue(this.formatDate(this.date));
11622                         this.hide();
11623                         break;
11624                 }
11625                 break;
11626             case 'span':
11627                 if (className.indexOf('disabled') === -1) {
11628                     this.viewDate.setUTCDate(1);
11629                     if (className.indexOf('month') !== -1) {
11630                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11631                     } else {
11632                         var year = parseInt(html, 10) || 0;
11633                         this.viewDate.setUTCFullYear(year);
11634                         
11635                     }
11636                     this.showMode(-1);
11637                     this.fill();
11638                 }
11639                 break;
11640                 
11641             case 'td':
11642                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11643                     var day = parseInt(html, 10) || 1;
11644                     var year = this.viewDate.getUTCFullYear(),
11645                         month = this.viewDate.getUTCMonth();
11646
11647                     if (className.indexOf('old') !== -1) {
11648                         if(month === 0 ){
11649                             month = 11;
11650                             year -= 1;
11651                         }else{
11652                             month -= 1;
11653                         }
11654                     } else if (className.indexOf('new') !== -1) {
11655                         if (month == 11) {
11656                             month = 0;
11657                             year += 1;
11658                         } else {
11659                             month += 1;
11660                         }
11661                     }
11662                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11663                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11664                     this.fill();
11665                     this.setValue(this.formatDate(this.date));
11666                     this.hide();
11667                 }
11668                 break;
11669         }
11670     },
11671     
11672     setStartDate: function(startDate){
11673         this.startDate = startDate || -Infinity;
11674         if (this.startDate !== -Infinity) {
11675             this.startDate = this.parseDate(this.startDate);
11676         }
11677         this.update();
11678         this.updateNavArrows();
11679     },
11680
11681     setEndDate: function(endDate){
11682         this.endDate = endDate || Infinity;
11683         if (this.endDate !== Infinity) {
11684             this.endDate = this.parseDate(this.endDate);
11685         }
11686         this.update();
11687         this.updateNavArrows();
11688     },
11689     
11690     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11691         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11692         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11693             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11694         }
11695         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11696             return parseInt(d, 10);
11697         });
11698         this.update();
11699         this.updateNavArrows();
11700     },
11701     
11702     updateNavArrows: function() {
11703         var d = new Date(this.viewDate),
11704         year = d.getUTCFullYear(),
11705         month = d.getUTCMonth();
11706         
11707         Roo.each(this.picker().select('.prev', true).elements, function(v){
11708             v.show();
11709             switch (this.viewMode) {
11710                 case 0:
11711
11712                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11713                         v.hide();
11714                     }
11715                     break;
11716                 case 1:
11717                 case 2:
11718                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11719                         v.hide();
11720                     }
11721                     break;
11722             }
11723         });
11724         
11725         Roo.each(this.picker().select('.next', true).elements, function(v){
11726             v.show();
11727             switch (this.viewMode) {
11728                 case 0:
11729
11730                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11731                         v.hide();
11732                     }
11733                     break;
11734                 case 1:
11735                 case 2:
11736                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11737                         v.hide();
11738                     }
11739                     break;
11740             }
11741         })
11742     },
11743     
11744     moveMonth: function(date, dir){
11745         if (!dir) return date;
11746         var new_date = new Date(date.valueOf()),
11747         day = new_date.getUTCDate(),
11748         month = new_date.getUTCMonth(),
11749         mag = Math.abs(dir),
11750         new_month, test;
11751         dir = dir > 0 ? 1 : -1;
11752         if (mag == 1){
11753             test = dir == -1
11754             // If going back one month, make sure month is not current month
11755             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11756             ? function(){
11757                 return new_date.getUTCMonth() == month;
11758             }
11759             // If going forward one month, make sure month is as expected
11760             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11761             : function(){
11762                 return new_date.getUTCMonth() != new_month;
11763             };
11764             new_month = month + dir;
11765             new_date.setUTCMonth(new_month);
11766             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11767             if (new_month < 0 || new_month > 11)
11768                 new_month = (new_month + 12) % 12;
11769         } else {
11770             // For magnitudes >1, move one month at a time...
11771             for (var i=0; i<mag; i++)
11772                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11773                 new_date = this.moveMonth(new_date, dir);
11774             // ...then reset the day, keeping it in the new month
11775             new_month = new_date.getUTCMonth();
11776             new_date.setUTCDate(day);
11777             test = function(){
11778                 return new_month != new_date.getUTCMonth();
11779             };
11780         }
11781         // Common date-resetting loop -- if date is beyond end of month, make it
11782         // end of month
11783         while (test()){
11784             new_date.setUTCDate(--day);
11785             new_date.setUTCMonth(new_month);
11786         }
11787         return new_date;
11788     },
11789
11790     moveYear: function(date, dir){
11791         return this.moveMonth(date, dir*12);
11792     },
11793
11794     dateWithinRange: function(date){
11795         return date >= this.startDate && date <= this.endDate;
11796     },
11797
11798     
11799     remove: function() {
11800         this.picker().remove();
11801     }
11802    
11803 });
11804
11805 Roo.apply(Roo.bootstrap.DateField,  {
11806     
11807     head : {
11808         tag: 'thead',
11809         cn: [
11810         {
11811             tag: 'tr',
11812             cn: [
11813             {
11814                 tag: 'th',
11815                 cls: 'prev',
11816                 html: '<i class="icon-arrow-left"/>'
11817             },
11818             {
11819                 tag: 'th',
11820                 cls: 'switch',
11821                 colspan: '5'
11822             },
11823             {
11824                 tag: 'th',
11825                 cls: 'next',
11826                 html: '<i class="icon-arrow-right"/>'
11827             }
11828
11829             ]
11830         }
11831         ]
11832     },
11833     
11834     content : {
11835         tag: 'tbody',
11836         cn: [
11837         {
11838             tag: 'tr',
11839             cn: [
11840             {
11841                 tag: 'td',
11842                 colspan: '7'
11843             }
11844             ]
11845         }
11846         ]
11847     },
11848     
11849     footer : {
11850         tag: 'tfoot',
11851         cn: [
11852         {
11853             tag: 'tr',
11854             cn: [
11855             {
11856                 tag: 'th',
11857                 colspan: '7',
11858                 cls: 'today'
11859             }
11860                     
11861             ]
11862         }
11863         ]
11864     },
11865     
11866     dates:{
11867         en: {
11868             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11869             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11870             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11871             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11872             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11873             today: "Today"
11874         }
11875     },
11876     
11877     modes: [
11878     {
11879         clsName: 'days',
11880         navFnc: 'Month',
11881         navStep: 1
11882     },
11883     {
11884         clsName: 'months',
11885         navFnc: 'FullYear',
11886         navStep: 1
11887     },
11888     {
11889         clsName: 'years',
11890         navFnc: 'FullYear',
11891         navStep: 10
11892     }]
11893 });
11894
11895 Roo.apply(Roo.bootstrap.DateField,  {
11896   
11897     template : {
11898         tag: 'div',
11899         cls: 'datepicker dropdown-menu',
11900         cn: [
11901         {
11902             tag: 'div',
11903             cls: 'datepicker-days',
11904             cn: [
11905             {
11906                 tag: 'table',
11907                 cls: 'table-condensed',
11908                 cn:[
11909                 Roo.bootstrap.DateField.head,
11910                 {
11911                     tag: 'tbody'
11912                 },
11913                 Roo.bootstrap.DateField.footer
11914                 ]
11915             }
11916             ]
11917         },
11918         {
11919             tag: 'div',
11920             cls: 'datepicker-months',
11921             cn: [
11922             {
11923                 tag: 'table',
11924                 cls: 'table-condensed',
11925                 cn:[
11926                 Roo.bootstrap.DateField.head,
11927                 Roo.bootstrap.DateField.content,
11928                 Roo.bootstrap.DateField.footer
11929                 ]
11930             }
11931             ]
11932         },
11933         {
11934             tag: 'div',
11935             cls: 'datepicker-years',
11936             cn: [
11937             {
11938                 tag: 'table',
11939                 cls: 'table-condensed',
11940                 cn:[
11941                 Roo.bootstrap.DateField.head,
11942                 Roo.bootstrap.DateField.content,
11943                 Roo.bootstrap.DateField.footer
11944                 ]
11945             }
11946             ]
11947         }
11948         ]
11949     }
11950 });
11951
11952  
11953
11954  /*
11955  * - LGPL
11956  *
11957  * TimeField
11958  * 
11959  */
11960
11961 /**
11962  * @class Roo.bootstrap.TimeField
11963  * @extends Roo.bootstrap.Input
11964  * Bootstrap DateField class
11965  * 
11966  * 
11967  * @constructor
11968  * Create a new TimeField
11969  * @param {Object} config The config object
11970  */
11971
11972 Roo.bootstrap.TimeField = function(config){
11973     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11974     this.addEvents({
11975             /**
11976              * @event show
11977              * Fires when this field show.
11978              * @param {Roo.bootstrap.DateField} this
11979              * @param {Mixed} date The date value
11980              */
11981             show : true,
11982             /**
11983              * @event show
11984              * Fires when this field hide.
11985              * @param {Roo.bootstrap.DateField} this
11986              * @param {Mixed} date The date value
11987              */
11988             hide : true,
11989             /**
11990              * @event select
11991              * Fires when select a date.
11992              * @param {Roo.bootstrap.DateField} this
11993              * @param {Mixed} date The date value
11994              */
11995             select : true
11996         });
11997 };
11998
11999 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
12000     
12001     /**
12002      * @cfg {String} format
12003      * The default time format string which can be overriden for localization support.  The format must be
12004      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12005      */
12006     format : "H:i",
12007        
12008     onRender: function(ct, position)
12009     {
12010         
12011         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12012                 
12013         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12014         
12015         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12016         
12017         this.pop = this.picker().select('>.datepicker-time',true).first();
12018         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12019         
12020         this.picker().on('mousedown', this.onMousedown, this);
12021         this.picker().on('click', this.onClick, this);
12022         
12023         this.picker().addClass('datepicker-dropdown');
12024     
12025         this.fillTime();
12026         this.update();
12027             
12028         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12029         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12030         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12031         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12032         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12033         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12034
12035     },
12036     
12037     fireKey: function(e){
12038         if (!this.picker().isVisible()){
12039             if (e.keyCode == 27) // allow escape to hide and re-show picker
12040                 this.show();
12041             return;
12042         }
12043
12044         e.preventDefault();
12045         
12046         switch(e.keyCode){
12047             case 27: // escape
12048                 this.hide();
12049                 break;
12050             case 37: // left
12051             case 39: // right
12052                 this.onTogglePeriod();
12053                 break;
12054             case 38: // up
12055                 this.onIncrementMinutes();
12056                 break;
12057             case 40: // down
12058                 this.onDecrementMinutes();
12059                 break;
12060             case 13: // enter
12061             case 9: // tab
12062                 this.setTime();
12063                 break;
12064         }
12065     },
12066     
12067     onClick: function(e) {
12068         e.stopPropagation();
12069         e.preventDefault();
12070     },
12071     
12072     picker : function()
12073     {
12074         return this.el.select('.datepicker', true).first();
12075     },
12076     
12077     fillTime: function()
12078     {    
12079         var time = this.pop.select('tbody', true).first();
12080         
12081         time.dom.innerHTML = '';
12082         
12083         time.createChild({
12084             tag: 'tr',
12085             cn: [
12086                 {
12087                     tag: 'td',
12088                     cn: [
12089                         {
12090                             tag: 'a',
12091                             href: '#',
12092                             cls: 'btn',
12093                             cn: [
12094                                 {
12095                                     tag: 'span',
12096                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12097                                 }
12098                             ]
12099                         } 
12100                     ]
12101                 },
12102                 {
12103                     tag: 'td',
12104                     cls: 'separator'
12105                 },
12106                 {
12107                     tag: 'td',
12108                     cn: [
12109                         {
12110                             tag: 'a',
12111                             href: '#',
12112                             cls: 'btn',
12113                             cn: [
12114                                 {
12115                                     tag: 'span',
12116                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12117                                 }
12118                             ]
12119                         }
12120                     ]
12121                 },
12122                 {
12123                     tag: 'td',
12124                     cls: 'separator'
12125                 }
12126             ]
12127         });
12128         
12129         time.createChild({
12130             tag: 'tr',
12131             cn: [
12132                 {
12133                     tag: 'td',
12134                     cn: [
12135                         {
12136                             tag: 'span',
12137                             cls: 'timepicker-hour',
12138                             html: '00'
12139                         }  
12140                     ]
12141                 },
12142                 {
12143                     tag: 'td',
12144                     cls: 'separator',
12145                     html: ':'
12146                 },
12147                 {
12148                     tag: 'td',
12149                     cn: [
12150                         {
12151                             tag: 'span',
12152                             cls: 'timepicker-minute',
12153                             html: '00'
12154                         }  
12155                     ]
12156                 },
12157                 {
12158                     tag: 'td',
12159                     cls: 'separator'
12160                 },
12161                 {
12162                     tag: 'td',
12163                     cn: [
12164                         {
12165                             tag: 'button',
12166                             type: 'button',
12167                             cls: 'btn btn-primary period',
12168                             html: 'AM'
12169                             
12170                         }
12171                     ]
12172                 }
12173             ]
12174         });
12175         
12176         time.createChild({
12177             tag: 'tr',
12178             cn: [
12179                 {
12180                     tag: 'td',
12181                     cn: [
12182                         {
12183                             tag: 'a',
12184                             href: '#',
12185                             cls: 'btn',
12186                             cn: [
12187                                 {
12188                                     tag: 'span',
12189                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12190                                 }
12191                             ]
12192                         }
12193                     ]
12194                 },
12195                 {
12196                     tag: 'td',
12197                     cls: 'separator'
12198                 },
12199                 {
12200                     tag: 'td',
12201                     cn: [
12202                         {
12203                             tag: 'a',
12204                             href: '#',
12205                             cls: 'btn',
12206                             cn: [
12207                                 {
12208                                     tag: 'span',
12209                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12210                                 }
12211                             ]
12212                         }
12213                     ]
12214                 },
12215                 {
12216                     tag: 'td',
12217                     cls: 'separator'
12218                 }
12219             ]
12220         });
12221         
12222     },
12223     
12224     update: function()
12225     {
12226         
12227         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12228         
12229         this.fill();
12230     },
12231     
12232     fill: function() 
12233     {
12234         var hours = this.time.getHours();
12235         var minutes = this.time.getMinutes();
12236         var period = 'AM';
12237         
12238         if(hours > 11){
12239             period = 'PM';
12240         }
12241         
12242         if(hours == 0){
12243             hours = 12;
12244         }
12245         
12246         
12247         if(hours > 12){
12248             hours = hours - 12;
12249         }
12250         
12251         if(hours < 10){
12252             hours = '0' + hours;
12253         }
12254         
12255         if(minutes < 10){
12256             minutes = '0' + minutes;
12257         }
12258         
12259         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12260         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12261         this.pop.select('button', true).first().dom.innerHTML = period;
12262         
12263     },
12264     
12265     place: function()
12266     {   
12267         this.picker().removeClass(['bottom', 'top']);
12268         
12269         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12270             /*
12271              * place to the top of element!
12272              *
12273              */
12274             
12275             this.picker().addClass('top');
12276             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12277             
12278             return;
12279         }
12280         
12281         this.picker().addClass('bottom');
12282         
12283         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12284     },
12285   
12286     onFocus : function()
12287     {
12288         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12289         this.show();
12290     },
12291     
12292     onBlur : function()
12293     {
12294         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12295         this.hide();
12296     },
12297     
12298     show : function()
12299     {
12300         this.picker().show();
12301         this.pop.show();
12302         this.update();
12303         this.place();
12304         
12305         this.fireEvent('show', this, this.date);
12306     },
12307     
12308     hide : function()
12309     {
12310         this.picker().hide();
12311         this.pop.hide();
12312         
12313         this.fireEvent('hide', this, this.date);
12314     },
12315     
12316     setTime : function()
12317     {
12318         this.hide();
12319         this.setValue(this.time.format(this.format));
12320         
12321         this.fireEvent('select', this, this.date);
12322         
12323         
12324     },
12325     
12326     onMousedown: function(e){
12327         e.stopPropagation();
12328         e.preventDefault();
12329     },
12330     
12331     onIncrementHours: function()
12332     {
12333         Roo.log('onIncrementHours');
12334         this.time = this.time.add(Date.HOUR, 1);
12335         this.update();
12336         
12337     },
12338     
12339     onDecrementHours: function()
12340     {
12341         Roo.log('onDecrementHours');
12342         this.time = this.time.add(Date.HOUR, -1);
12343         this.update();
12344     },
12345     
12346     onIncrementMinutes: function()
12347     {
12348         Roo.log('onIncrementMinutes');
12349         this.time = this.time.add(Date.MINUTE, 1);
12350         this.update();
12351     },
12352     
12353     onDecrementMinutes: function()
12354     {
12355         Roo.log('onDecrementMinutes');
12356         this.time = this.time.add(Date.MINUTE, -1);
12357         this.update();
12358     },
12359     
12360     onTogglePeriod: function()
12361     {
12362         Roo.log('onTogglePeriod');
12363         this.time = this.time.add(Date.HOUR, 12);
12364         this.update();
12365     }
12366     
12367    
12368 });
12369
12370 Roo.apply(Roo.bootstrap.TimeField,  {
12371     
12372     content : {
12373         tag: 'tbody',
12374         cn: [
12375             {
12376                 tag: 'tr',
12377                 cn: [
12378                 {
12379                     tag: 'td',
12380                     colspan: '7'
12381                 }
12382                 ]
12383             }
12384         ]
12385     },
12386     
12387     footer : {
12388         tag: 'tfoot',
12389         cn: [
12390             {
12391                 tag: 'tr',
12392                 cn: [
12393                 {
12394                     tag: 'th',
12395                     colspan: '7',
12396                     cls: '',
12397                     cn: [
12398                         {
12399                             tag: 'button',
12400                             cls: 'btn btn-info ok',
12401                             html: 'OK'
12402                         }
12403                     ]
12404                 }
12405
12406                 ]
12407             }
12408         ]
12409     }
12410 });
12411
12412 Roo.apply(Roo.bootstrap.TimeField,  {
12413   
12414     template : {
12415         tag: 'div',
12416         cls: 'datepicker dropdown-menu',
12417         cn: [
12418             {
12419                 tag: 'div',
12420                 cls: 'datepicker-time',
12421                 cn: [
12422                 {
12423                     tag: 'table',
12424                     cls: 'table-condensed',
12425                     cn:[
12426                     Roo.bootstrap.TimeField.content,
12427                     Roo.bootstrap.TimeField.footer
12428                     ]
12429                 }
12430                 ]
12431             }
12432         ]
12433     }
12434 });
12435
12436  
12437
12438  /*
12439  * - LGPL
12440  *
12441  * CheckBox
12442  * 
12443  */
12444
12445 /**
12446  * @class Roo.bootstrap.CheckBox
12447  * @extends Roo.bootstrap.Input
12448  * Bootstrap CheckBox class
12449  * 
12450  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12451  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12452  * @cfg {String} boxLabel The text that appears beside the checkbox
12453  * @cfg {Boolean} checked initnal the element
12454  * 
12455  * @constructor
12456  * Create a new CheckBox
12457  * @param {Object} config The config object
12458  */
12459
12460 Roo.bootstrap.CheckBox = function(config){
12461     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12462    
12463         this.addEvents({
12464             /**
12465             * @event check
12466             * Fires when the element is checked or unchecked.
12467             * @param {Roo.bootstrap.CheckBox} this This input
12468             * @param {Boolean} checked The new checked value
12469             */
12470            check : true
12471         });
12472 };
12473
12474 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12475     
12476     inputType: 'checkbox',
12477     inputValue: 1,
12478     valueOff: 0,
12479     boxLabel: false,
12480     checked: false,
12481     
12482     getAutoCreate : function()
12483     {
12484         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12485         
12486         var id = Roo.id();
12487         
12488         var cfg = {};
12489         
12490         cfg.cls = 'form-group' //input-group
12491         
12492         var input =  {
12493             tag: 'input',
12494             id : id,
12495             type : this.inputType,
12496             value : (!this.checked) ? this.valueOff : this.inputValue,
12497             cls : 'form-box',
12498             placeholder : this.placeholder || ''
12499             
12500         };
12501         
12502         if (this.disabled) {
12503             input.disabled=true;
12504         }
12505         
12506         if(this.checked){
12507             input.checked = this.checked;
12508         }
12509         
12510         if (this.name) {
12511             input.name = this.name;
12512         }
12513         
12514         if (this.size) {
12515             input.cls += ' input-' + this.size;
12516         }
12517         
12518         var settings=this;
12519         ['xs','sm','md','lg'].map(function(size){
12520             if (settings[size]) {
12521                 cfg.cls += ' col-' + size + '-' + settings[size];
12522             }
12523         });
12524         
12525         var inputblock = input;
12526         
12527         if (this.before || this.after) {
12528             
12529             inputblock = {
12530                 cls : 'input-group',
12531                 cn :  [] 
12532             };
12533             if (this.before) {
12534                 inputblock.cn.push({
12535                     tag :'span',
12536                     cls : 'input-group-addon',
12537                     html : this.before
12538                 });
12539             }
12540             inputblock.cn.push(input);
12541             if (this.after) {
12542                 inputblock.cn.push({
12543                     tag :'span',
12544                     cls : 'input-group-addon',
12545                     html : this.after
12546                 });
12547             }
12548             
12549         };
12550         
12551         if (align ==='left' && this.fieldLabel.length) {
12552                 Roo.log("left and has label");
12553                 cfg.cn = [
12554                     
12555                     {
12556                         tag: 'label',
12557                         'for' :  id,
12558                         cls : 'control-label col-md-' + this.labelWidth,
12559                         html : this.fieldLabel
12560                         
12561                     },
12562                     {
12563                         cls : "col-md-" + (12 - this.labelWidth), 
12564                         cn: [
12565                             inputblock
12566                         ]
12567                     }
12568                     
12569                 ];
12570         } else if ( this.fieldLabel.length) {
12571                 Roo.log(" label");
12572                 cfg.cn = [
12573                    
12574                     {
12575                         tag: this.boxLabel ? 'span' : 'label',
12576                         'for': id,
12577                         cls: 'control-label box-input-label',
12578                         //cls : 'input-group-addon',
12579                         html : this.fieldLabel
12580                         
12581                     },
12582                     
12583                     inputblock
12584                     
12585                 ];
12586
12587         } else {
12588             
12589                    Roo.log(" no label && no align");
12590                 cfg.cn = [
12591                     
12592                         inputblock
12593                     
12594                 ];
12595                 
12596                 
12597         };
12598         
12599         if(this.boxLabel){
12600             cfg.cn.push({
12601                 tag: 'label',
12602                 'for': id,
12603                 cls: 'box-label',
12604                 html: this.boxLabel
12605             })
12606         }
12607         
12608         return cfg;
12609         
12610     },
12611     
12612     /**
12613      * return the real input element.
12614      */
12615     inputEl: function ()
12616     {
12617         return this.el.select('input.form-box',true).first();
12618     },
12619     
12620     initEvents : function()
12621     {
12622 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12623         
12624         this.inputEl().on('click', this.onClick,  this);
12625         
12626     },
12627     
12628     onClick : function()
12629     {   
12630         this.setChecked(!this.checked);
12631     },
12632     
12633     setChecked : function(state,suppressEvent)
12634     {
12635         this.checked = state;
12636         
12637         this.inputEl().dom.checked = state;
12638         
12639         if(suppressEvent !== true){
12640             this.fireEvent('check', this, state);
12641         }
12642         
12643         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12644         
12645     },
12646     
12647     setValue : function(v,suppressEvent)
12648     {
12649         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12650     }
12651     
12652 });
12653
12654  
12655 /*
12656  * - LGPL
12657  *
12658  * Radio
12659  * 
12660  */
12661
12662 /**
12663  * @class Roo.bootstrap.Radio
12664  * @extends Roo.bootstrap.CheckBox
12665  * Bootstrap Radio class
12666
12667  * @constructor
12668  * Create a new Radio
12669  * @param {Object} config The config object
12670  */
12671
12672 Roo.bootstrap.Radio = function(config){
12673     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12674    
12675 };
12676
12677 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12678     
12679     inputType: 'radio',
12680     inputValue: '',
12681     valueOff: '',
12682     
12683     getAutoCreate : function()
12684     {
12685         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12686         
12687         var id = Roo.id();
12688         
12689         var cfg = {};
12690         
12691         cfg.cls = 'form-group' //input-group
12692         
12693         var input =  {
12694             tag: 'input',
12695             id : id,
12696             type : this.inputType,
12697             value : (!this.checked) ? this.valueOff : this.inputValue,
12698             cls : 'form-box',
12699             placeholder : this.placeholder || ''
12700             
12701         };
12702         
12703         if (this.disabled) {
12704             input.disabled=true;
12705         }
12706         
12707         if(this.checked){
12708             input.checked = this.checked;
12709         }
12710         
12711         if (this.name) {
12712             input.name = this.name;
12713         }
12714         
12715         if (this.size) {
12716             input.cls += ' input-' + this.size;
12717         }
12718         
12719         var settings=this;
12720         ['xs','sm','md','lg'].map(function(size){
12721             if (settings[size]) {
12722                 cfg.cls += ' col-' + size + '-' + settings[size];
12723             }
12724         });
12725         
12726         var inputblock = input;
12727         
12728         if (this.before || this.after) {
12729             
12730             inputblock = {
12731                 cls : 'input-group',
12732                 cn :  [] 
12733             };
12734             if (this.before) {
12735                 inputblock.cn.push({
12736                     tag :'span',
12737                     cls : 'input-group-addon',
12738                     html : this.before
12739                 });
12740             }
12741             inputblock.cn.push(input);
12742             if (this.after) {
12743                 inputblock.cn.push({
12744                     tag :'span',
12745                     cls : 'input-group-addon',
12746                     html : this.after
12747                 });
12748             }
12749             
12750         };
12751         
12752         if (align ==='left' && this.fieldLabel.length) {
12753                 Roo.log("left and has label");
12754                 cfg.cn = [
12755                     
12756                     {
12757                         tag: 'label',
12758                         'for' :  id,
12759                         cls : 'control-label col-md-' + this.labelWidth,
12760                         html : this.fieldLabel
12761                         
12762                     },
12763                     {
12764                         cls : "col-md-" + (12 - this.labelWidth), 
12765                         cn: [
12766                             inputblock
12767                         ]
12768                     }
12769                     
12770                 ];
12771         } else if ( this.fieldLabel.length) {
12772                 Roo.log(" label");
12773                  cfg.cn = [
12774                    
12775                     {
12776                         tag: 'label',
12777                         'for': id,
12778                         cls: 'control-label box-input-label',
12779                         //cls : 'input-group-addon',
12780                         html : this.fieldLabel
12781                         
12782                     },
12783                     
12784                     inputblock
12785                     
12786                 ];
12787
12788         } else {
12789             
12790                    Roo.log(" no label && no align");
12791                 cfg.cn = [
12792                     
12793                         inputblock
12794                     
12795                 ];
12796                 
12797                 
12798         };
12799         
12800         if(this.boxLabel){
12801             cfg.cn.push({
12802                 tag: 'label',
12803                 'for': id,
12804                 cls: 'box-label',
12805                 html: this.boxLabel
12806             })
12807         }
12808         
12809         return cfg;
12810         
12811     },
12812    
12813     onClick : function()
12814     {   
12815         this.setChecked(true);
12816     },
12817     
12818     setChecked : function(state,suppressEvent)
12819     {
12820         if(state){
12821             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12822                 v.dom.checked = false;
12823             });
12824         }
12825         
12826         this.checked = state;
12827         this.inputEl().dom.checked = state;
12828         
12829         if(suppressEvent !== true){
12830             this.fireEvent('check', this, state);
12831         }
12832         
12833         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12834         
12835     },
12836     
12837     getGroupValue : function()
12838     {
12839         var value = ''
12840         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12841             if(v.dom.checked == true){
12842                 value = v.dom.value;
12843             }
12844         });
12845         
12846         return value;
12847     },
12848     
12849     /**
12850      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12851      * @return {Mixed} value The field value
12852      */
12853     getValue : function(){
12854         return this.getGroupValue();
12855     }
12856     
12857 });
12858
12859  
12860 //<script type="text/javascript">
12861
12862 /*
12863  * Based  Ext JS Library 1.1.1
12864  * Copyright(c) 2006-2007, Ext JS, LLC.
12865  * LGPL
12866  *
12867  */
12868  
12869 /**
12870  * @class Roo.HtmlEditorCore
12871  * @extends Roo.Component
12872  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12873  *
12874  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12875  */
12876
12877 Roo.HtmlEditorCore = function(config){
12878     
12879     
12880     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12881     this.addEvents({
12882         /**
12883          * @event initialize
12884          * Fires when the editor is fully initialized (including the iframe)
12885          * @param {Roo.HtmlEditorCore} this
12886          */
12887         initialize: true,
12888         /**
12889          * @event activate
12890          * Fires when the editor is first receives the focus. Any insertion must wait
12891          * until after this event.
12892          * @param {Roo.HtmlEditorCore} this
12893          */
12894         activate: true,
12895          /**
12896          * @event beforesync
12897          * Fires before the textarea is updated with content from the editor iframe. Return false
12898          * to cancel the sync.
12899          * @param {Roo.HtmlEditorCore} this
12900          * @param {String} html
12901          */
12902         beforesync: true,
12903          /**
12904          * @event beforepush
12905          * Fires before the iframe editor is updated with content from the textarea. Return false
12906          * to cancel the push.
12907          * @param {Roo.HtmlEditorCore} this
12908          * @param {String} html
12909          */
12910         beforepush: true,
12911          /**
12912          * @event sync
12913          * Fires when the textarea is updated with content from the editor iframe.
12914          * @param {Roo.HtmlEditorCore} this
12915          * @param {String} html
12916          */
12917         sync: true,
12918          /**
12919          * @event push
12920          * Fires when the iframe editor is updated with content from the textarea.
12921          * @param {Roo.HtmlEditorCore} this
12922          * @param {String} html
12923          */
12924         push: true,
12925         
12926         /**
12927          * @event editorevent
12928          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12929          * @param {Roo.HtmlEditorCore} this
12930          */
12931         editorevent: true
12932     });
12933      
12934 };
12935
12936
12937 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12938
12939
12940      /**
12941      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12942      */
12943     
12944     owner : false,
12945     
12946      /**
12947      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12948      *                        Roo.resizable.
12949      */
12950     resizable : false,
12951      /**
12952      * @cfg {Number} height (in pixels)
12953      */   
12954     height: 300,
12955    /**
12956      * @cfg {Number} width (in pixels)
12957      */   
12958     width: 500,
12959     
12960     /**
12961      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12962      * 
12963      */
12964     stylesheets: false,
12965     
12966     // id of frame..
12967     frameId: false,
12968     
12969     // private properties
12970     validationEvent : false,
12971     deferHeight: true,
12972     initialized : false,
12973     activated : false,
12974     sourceEditMode : false,
12975     onFocus : Roo.emptyFn,
12976     iframePad:3,
12977     hideMode:'offsets',
12978     
12979     clearUp: true,
12980     
12981      
12982     
12983
12984     /**
12985      * Protected method that will not generally be called directly. It
12986      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12987      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12988      */
12989     getDocMarkup : function(){
12990         // body styles..
12991         var st = '';
12992         Roo.log(this.stylesheets);
12993         
12994         // inherit styels from page...?? 
12995         if (this.stylesheets === false) {
12996             
12997             Roo.get(document.head).select('style').each(function(node) {
12998                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12999             });
13000             
13001             Roo.get(document.head).select('link').each(function(node) { 
13002                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13003             });
13004             
13005         } else if (!this.stylesheets.length) {
13006                 // simple..
13007                 st = '<style type="text/css">' +
13008                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13009                    '</style>';
13010         } else {
13011             Roo.each(this.stylesheets, function(s) {
13012                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13013             });
13014             
13015         }
13016         
13017         st +=  '<style type="text/css">' +
13018             'IMG { cursor: pointer } ' +
13019         '</style>';
13020
13021         
13022         return '<html><head>' + st  +
13023             //<style type="text/css">' +
13024             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13025             //'</style>' +
13026             ' </head><body class="roo-htmleditor-body"></body></html>';
13027     },
13028
13029     // private
13030     onRender : function(ct, position)
13031     {
13032         var _t = this;
13033         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13034         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13035         
13036         
13037         this.el.dom.style.border = '0 none';
13038         this.el.dom.setAttribute('tabIndex', -1);
13039         this.el.addClass('x-hidden hide');
13040         
13041         
13042         
13043         if(Roo.isIE){ // fix IE 1px bogus margin
13044             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13045         }
13046        
13047         
13048         this.frameId = Roo.id();
13049         
13050          
13051         
13052         var iframe = this.owner.wrap.createChild({
13053             tag: 'iframe',
13054             cls: 'form-control', // bootstrap..
13055             id: this.frameId,
13056             name: this.frameId,
13057             frameBorder : 'no',
13058             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13059         }, this.el
13060         );
13061         
13062         
13063         this.iframe = iframe.dom;
13064
13065          this.assignDocWin();
13066         
13067         this.doc.designMode = 'on';
13068        
13069         this.doc.open();
13070         this.doc.write(this.getDocMarkup());
13071         this.doc.close();
13072
13073         
13074         var task = { // must defer to wait for browser to be ready
13075             run : function(){
13076                 //console.log("run task?" + this.doc.readyState);
13077                 this.assignDocWin();
13078                 if(this.doc.body || this.doc.readyState == 'complete'){
13079                     try {
13080                         this.doc.designMode="on";
13081                     } catch (e) {
13082                         return;
13083                     }
13084                     Roo.TaskMgr.stop(task);
13085                     this.initEditor.defer(10, this);
13086                 }
13087             },
13088             interval : 10,
13089             duration: 10000,
13090             scope: this
13091         };
13092         Roo.TaskMgr.start(task);
13093
13094         
13095          
13096     },
13097
13098     // private
13099     onResize : function(w, h)
13100     {
13101          Roo.log('resize: ' +w + ',' + h );
13102         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13103         if(!this.iframe){
13104             return;
13105         }
13106         if(typeof w == 'number'){
13107             
13108             this.iframe.style.width = w + 'px';
13109         }
13110         if(typeof h == 'number'){
13111             
13112             this.iframe.style.height = h + 'px';
13113             if(this.doc){
13114                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13115             }
13116         }
13117         
13118     },
13119
13120     /**
13121      * Toggles the editor between standard and source edit mode.
13122      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13123      */
13124     toggleSourceEdit : function(sourceEditMode){
13125         
13126         this.sourceEditMode = sourceEditMode === true;
13127         
13128         if(this.sourceEditMode){
13129  
13130             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13131             
13132         }else{
13133             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13134             //this.iframe.className = '';
13135             this.deferFocus();
13136         }
13137         //this.setSize(this.owner.wrap.getSize());
13138         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13139     },
13140
13141     
13142   
13143
13144     /**
13145      * Protected method that will not generally be called directly. If you need/want
13146      * custom HTML cleanup, this is the method you should override.
13147      * @param {String} html The HTML to be cleaned
13148      * return {String} The cleaned HTML
13149      */
13150     cleanHtml : function(html){
13151         html = String(html);
13152         if(html.length > 5){
13153             if(Roo.isSafari){ // strip safari nonsense
13154                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13155             }
13156         }
13157         if(html == '&nbsp;'){
13158             html = '';
13159         }
13160         return html;
13161     },
13162
13163     /**
13164      * HTML Editor -> Textarea
13165      * Protected method that will not generally be called directly. Syncs the contents
13166      * of the editor iframe with the textarea.
13167      */
13168     syncValue : function(){
13169         if(this.initialized){
13170             var bd = (this.doc.body || this.doc.documentElement);
13171             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13172             var html = bd.innerHTML;
13173             if(Roo.isSafari){
13174                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13175                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13176                 if(m && m[1]){
13177                     html = '<div style="'+m[0]+'">' + html + '</div>';
13178                 }
13179             }
13180             html = this.cleanHtml(html);
13181             // fix up the special chars.. normaly like back quotes in word...
13182             // however we do not want to do this with chinese..
13183             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13184                 var cc = b.charCodeAt();
13185                 if (
13186                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13187                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13188                     (cc >= 0xf900 && cc < 0xfb00 )
13189                 ) {
13190                         return b;
13191                 }
13192                 return "&#"+cc+";" 
13193             });
13194             if(this.owner.fireEvent('beforesync', this, html) !== false){
13195                 this.el.dom.value = html;
13196                 this.owner.fireEvent('sync', this, html);
13197             }
13198         }
13199     },
13200
13201     /**
13202      * Protected method that will not generally be called directly. Pushes the value of the textarea
13203      * into the iframe editor.
13204      */
13205     pushValue : function(){
13206         if(this.initialized){
13207             var v = this.el.dom.value.trim();
13208             
13209 //            if(v.length < 1){
13210 //                v = '&#160;';
13211 //            }
13212             
13213             if(this.owner.fireEvent('beforepush', this, v) !== false){
13214                 var d = (this.doc.body || this.doc.documentElement);
13215                 d.innerHTML = v;
13216                 this.cleanUpPaste();
13217                 this.el.dom.value = d.innerHTML;
13218                 this.owner.fireEvent('push', this, v);
13219             }
13220         }
13221     },
13222
13223     // private
13224     deferFocus : function(){
13225         this.focus.defer(10, this);
13226     },
13227
13228     // doc'ed in Field
13229     focus : function(){
13230         if(this.win && !this.sourceEditMode){
13231             this.win.focus();
13232         }else{
13233             this.el.focus();
13234         }
13235     },
13236     
13237     assignDocWin: function()
13238     {
13239         var iframe = this.iframe;
13240         
13241          if(Roo.isIE){
13242             this.doc = iframe.contentWindow.document;
13243             this.win = iframe.contentWindow;
13244         } else {
13245             if (!Roo.get(this.frameId)) {
13246                 return;
13247             }
13248             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13249             this.win = Roo.get(this.frameId).dom.contentWindow;
13250         }
13251     },
13252     
13253     // private
13254     initEditor : function(){
13255         //console.log("INIT EDITOR");
13256         this.assignDocWin();
13257         
13258         
13259         
13260         this.doc.designMode="on";
13261         this.doc.open();
13262         this.doc.write(this.getDocMarkup());
13263         this.doc.close();
13264         
13265         var dbody = (this.doc.body || this.doc.documentElement);
13266         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13267         // this copies styles from the containing element into thsi one..
13268         // not sure why we need all of this..
13269         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13270         ss['background-attachment'] = 'fixed'; // w3c
13271         dbody.bgProperties = 'fixed'; // ie
13272         Roo.DomHelper.applyStyles(dbody, ss);
13273         Roo.EventManager.on(this.doc, {
13274             //'mousedown': this.onEditorEvent,
13275             'mouseup': this.onEditorEvent,
13276             'dblclick': this.onEditorEvent,
13277             'click': this.onEditorEvent,
13278             'keyup': this.onEditorEvent,
13279             buffer:100,
13280             scope: this
13281         });
13282         if(Roo.isGecko){
13283             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13284         }
13285         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13286             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13287         }
13288         this.initialized = true;
13289
13290         this.owner.fireEvent('initialize', this);
13291         this.pushValue();
13292     },
13293
13294     // private
13295     onDestroy : function(){
13296         
13297         
13298         
13299         if(this.rendered){
13300             
13301             //for (var i =0; i < this.toolbars.length;i++) {
13302             //    // fixme - ask toolbars for heights?
13303             //    this.toolbars[i].onDestroy();
13304            // }
13305             
13306             //this.wrap.dom.innerHTML = '';
13307             //this.wrap.remove();
13308         }
13309     },
13310
13311     // private
13312     onFirstFocus : function(){
13313         
13314         this.assignDocWin();
13315         
13316         
13317         this.activated = true;
13318          
13319     
13320         if(Roo.isGecko){ // prevent silly gecko errors
13321             this.win.focus();
13322             var s = this.win.getSelection();
13323             if(!s.focusNode || s.focusNode.nodeType != 3){
13324                 var r = s.getRangeAt(0);
13325                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13326                 r.collapse(true);
13327                 this.deferFocus();
13328             }
13329             try{
13330                 this.execCmd('useCSS', true);
13331                 this.execCmd('styleWithCSS', false);
13332             }catch(e){}
13333         }
13334         this.owner.fireEvent('activate', this);
13335     },
13336
13337     // private
13338     adjustFont: function(btn){
13339         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13340         //if(Roo.isSafari){ // safari
13341         //    adjust *= 2;
13342        // }
13343         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13344         if(Roo.isSafari){ // safari
13345             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13346             v =  (v < 10) ? 10 : v;
13347             v =  (v > 48) ? 48 : v;
13348             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13349             
13350         }
13351         
13352         
13353         v = Math.max(1, v+adjust);
13354         
13355         this.execCmd('FontSize', v  );
13356     },
13357
13358     onEditorEvent : function(e){
13359         this.owner.fireEvent('editorevent', this, e);
13360       //  this.updateToolbar();
13361         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13362     },
13363
13364     insertTag : function(tg)
13365     {
13366         // could be a bit smarter... -> wrap the current selected tRoo..
13367         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13368             
13369             range = this.createRange(this.getSelection());
13370             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13371             wrappingNode.appendChild(range.extractContents());
13372             range.insertNode(wrappingNode);
13373
13374             return;
13375             
13376             
13377             
13378         }
13379         this.execCmd("formatblock",   tg);
13380         
13381     },
13382     
13383     insertText : function(txt)
13384     {
13385         
13386         
13387         var range = this.createRange();
13388         range.deleteContents();
13389                //alert(Sender.getAttribute('label'));
13390                
13391         range.insertNode(this.doc.createTextNode(txt));
13392     } ,
13393     
13394      
13395
13396     /**
13397      * Executes a Midas editor command on the editor document and performs necessary focus and
13398      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13399      * @param {String} cmd The Midas command
13400      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13401      */
13402     relayCmd : function(cmd, value){
13403         this.win.focus();
13404         this.execCmd(cmd, value);
13405         this.owner.fireEvent('editorevent', this);
13406         //this.updateToolbar();
13407         this.owner.deferFocus();
13408     },
13409
13410     /**
13411      * Executes a Midas editor command directly on the editor document.
13412      * For visual commands, you should use {@link #relayCmd} instead.
13413      * <b>This should only be called after the editor is initialized.</b>
13414      * @param {String} cmd The Midas command
13415      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13416      */
13417     execCmd : function(cmd, value){
13418         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13419         this.syncValue();
13420     },
13421  
13422  
13423    
13424     /**
13425      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13426      * to insert tRoo.
13427      * @param {String} text | dom node.. 
13428      */
13429     insertAtCursor : function(text)
13430     {
13431         
13432         
13433         
13434         if(!this.activated){
13435             return;
13436         }
13437         /*
13438         if(Roo.isIE){
13439             this.win.focus();
13440             var r = this.doc.selection.createRange();
13441             if(r){
13442                 r.collapse(true);
13443                 r.pasteHTML(text);
13444                 this.syncValue();
13445                 this.deferFocus();
13446             
13447             }
13448             return;
13449         }
13450         */
13451         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13452             this.win.focus();
13453             
13454             
13455             // from jquery ui (MIT licenced)
13456             var range, node;
13457             var win = this.win;
13458             
13459             if (win.getSelection && win.getSelection().getRangeAt) {
13460                 range = win.getSelection().getRangeAt(0);
13461                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13462                 range.insertNode(node);
13463             } else if (win.document.selection && win.document.selection.createRange) {
13464                 // no firefox support
13465                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13466                 win.document.selection.createRange().pasteHTML(txt);
13467             } else {
13468                 // no firefox support
13469                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13470                 this.execCmd('InsertHTML', txt);
13471             } 
13472             
13473             this.syncValue();
13474             
13475             this.deferFocus();
13476         }
13477     },
13478  // private
13479     mozKeyPress : function(e){
13480         if(e.ctrlKey){
13481             var c = e.getCharCode(), cmd;
13482           
13483             if(c > 0){
13484                 c = String.fromCharCode(c).toLowerCase();
13485                 switch(c){
13486                     case 'b':
13487                         cmd = 'bold';
13488                         break;
13489                     case 'i':
13490                         cmd = 'italic';
13491                         break;
13492                     
13493                     case 'u':
13494                         cmd = 'underline';
13495                         break;
13496                     
13497                     case 'v':
13498                         this.cleanUpPaste.defer(100, this);
13499                         return;
13500                         
13501                 }
13502                 if(cmd){
13503                     this.win.focus();
13504                     this.execCmd(cmd);
13505                     this.deferFocus();
13506                     e.preventDefault();
13507                 }
13508                 
13509             }
13510         }
13511     },
13512
13513     // private
13514     fixKeys : function(){ // load time branching for fastest keydown performance
13515         if(Roo.isIE){
13516             return function(e){
13517                 var k = e.getKey(), r;
13518                 if(k == e.TAB){
13519                     e.stopEvent();
13520                     r = this.doc.selection.createRange();
13521                     if(r){
13522                         r.collapse(true);
13523                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13524                         this.deferFocus();
13525                     }
13526                     return;
13527                 }
13528                 
13529                 if(k == e.ENTER){
13530                     r = this.doc.selection.createRange();
13531                     if(r){
13532                         var target = r.parentElement();
13533                         if(!target || target.tagName.toLowerCase() != 'li'){
13534                             e.stopEvent();
13535                             r.pasteHTML('<br />');
13536                             r.collapse(false);
13537                             r.select();
13538                         }
13539                     }
13540                 }
13541                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13542                     this.cleanUpPaste.defer(100, this);
13543                     return;
13544                 }
13545                 
13546                 
13547             };
13548         }else if(Roo.isOpera){
13549             return function(e){
13550                 var k = e.getKey();
13551                 if(k == e.TAB){
13552                     e.stopEvent();
13553                     this.win.focus();
13554                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13555                     this.deferFocus();
13556                 }
13557                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13558                     this.cleanUpPaste.defer(100, this);
13559                     return;
13560                 }
13561                 
13562             };
13563         }else if(Roo.isSafari){
13564             return function(e){
13565                 var k = e.getKey();
13566                 
13567                 if(k == e.TAB){
13568                     e.stopEvent();
13569                     this.execCmd('InsertText','\t');
13570                     this.deferFocus();
13571                     return;
13572                 }
13573                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13574                     this.cleanUpPaste.defer(100, this);
13575                     return;
13576                 }
13577                 
13578              };
13579         }
13580     }(),
13581     
13582     getAllAncestors: function()
13583     {
13584         var p = this.getSelectedNode();
13585         var a = [];
13586         if (!p) {
13587             a.push(p); // push blank onto stack..
13588             p = this.getParentElement();
13589         }
13590         
13591         
13592         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13593             a.push(p);
13594             p = p.parentNode;
13595         }
13596         a.push(this.doc.body);
13597         return a;
13598     },
13599     lastSel : false,
13600     lastSelNode : false,
13601     
13602     
13603     getSelection : function() 
13604     {
13605         this.assignDocWin();
13606         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13607     },
13608     
13609     getSelectedNode: function() 
13610     {
13611         // this may only work on Gecko!!!
13612         
13613         // should we cache this!!!!
13614         
13615         
13616         
13617          
13618         var range = this.createRange(this.getSelection()).cloneRange();
13619         
13620         if (Roo.isIE) {
13621             var parent = range.parentElement();
13622             while (true) {
13623                 var testRange = range.duplicate();
13624                 testRange.moveToElementText(parent);
13625                 if (testRange.inRange(range)) {
13626                     break;
13627                 }
13628                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13629                     break;
13630                 }
13631                 parent = parent.parentElement;
13632             }
13633             return parent;
13634         }
13635         
13636         // is ancestor a text element.
13637         var ac =  range.commonAncestorContainer;
13638         if (ac.nodeType == 3) {
13639             ac = ac.parentNode;
13640         }
13641         
13642         var ar = ac.childNodes;
13643          
13644         var nodes = [];
13645         var other_nodes = [];
13646         var has_other_nodes = false;
13647         for (var i=0;i<ar.length;i++) {
13648             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13649                 continue;
13650             }
13651             // fullly contained node.
13652             
13653             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13654                 nodes.push(ar[i]);
13655                 continue;
13656             }
13657             
13658             // probably selected..
13659             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13660                 other_nodes.push(ar[i]);
13661                 continue;
13662             }
13663             // outer..
13664             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13665                 continue;
13666             }
13667             
13668             
13669             has_other_nodes = true;
13670         }
13671         if (!nodes.length && other_nodes.length) {
13672             nodes= other_nodes;
13673         }
13674         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13675             return false;
13676         }
13677         
13678         return nodes[0];
13679     },
13680     createRange: function(sel)
13681     {
13682         // this has strange effects when using with 
13683         // top toolbar - not sure if it's a great idea.
13684         //this.editor.contentWindow.focus();
13685         if (typeof sel != "undefined") {
13686             try {
13687                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13688             } catch(e) {
13689                 return this.doc.createRange();
13690             }
13691         } else {
13692             return this.doc.createRange();
13693         }
13694     },
13695     getParentElement: function()
13696     {
13697         
13698         this.assignDocWin();
13699         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13700         
13701         var range = this.createRange(sel);
13702          
13703         try {
13704             var p = range.commonAncestorContainer;
13705             while (p.nodeType == 3) { // text node
13706                 p = p.parentNode;
13707             }
13708             return p;
13709         } catch (e) {
13710             return null;
13711         }
13712     
13713     },
13714     /***
13715      *
13716      * Range intersection.. the hard stuff...
13717      *  '-1' = before
13718      *  '0' = hits..
13719      *  '1' = after.
13720      *         [ -- selected range --- ]
13721      *   [fail]                        [fail]
13722      *
13723      *    basically..
13724      *      if end is before start or  hits it. fail.
13725      *      if start is after end or hits it fail.
13726      *
13727      *   if either hits (but other is outside. - then it's not 
13728      *   
13729      *    
13730      **/
13731     
13732     
13733     // @see http://www.thismuchiknow.co.uk/?p=64.
13734     rangeIntersectsNode : function(range, node)
13735     {
13736         var nodeRange = node.ownerDocument.createRange();
13737         try {
13738             nodeRange.selectNode(node);
13739         } catch (e) {
13740             nodeRange.selectNodeContents(node);
13741         }
13742     
13743         var rangeStartRange = range.cloneRange();
13744         rangeStartRange.collapse(true);
13745     
13746         var rangeEndRange = range.cloneRange();
13747         rangeEndRange.collapse(false);
13748     
13749         var nodeStartRange = nodeRange.cloneRange();
13750         nodeStartRange.collapse(true);
13751     
13752         var nodeEndRange = nodeRange.cloneRange();
13753         nodeEndRange.collapse(false);
13754     
13755         return rangeStartRange.compareBoundaryPoints(
13756                  Range.START_TO_START, nodeEndRange) == -1 &&
13757                rangeEndRange.compareBoundaryPoints(
13758                  Range.START_TO_START, nodeStartRange) == 1;
13759         
13760          
13761     },
13762     rangeCompareNode : function(range, node)
13763     {
13764         var nodeRange = node.ownerDocument.createRange();
13765         try {
13766             nodeRange.selectNode(node);
13767         } catch (e) {
13768             nodeRange.selectNodeContents(node);
13769         }
13770         
13771         
13772         range.collapse(true);
13773     
13774         nodeRange.collapse(true);
13775      
13776         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13777         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13778          
13779         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13780         
13781         var nodeIsBefore   =  ss == 1;
13782         var nodeIsAfter    = ee == -1;
13783         
13784         if (nodeIsBefore && nodeIsAfter)
13785             return 0; // outer
13786         if (!nodeIsBefore && nodeIsAfter)
13787             return 1; //right trailed.
13788         
13789         if (nodeIsBefore && !nodeIsAfter)
13790             return 2;  // left trailed.
13791         // fully contined.
13792         return 3;
13793     },
13794
13795     // private? - in a new class?
13796     cleanUpPaste :  function()
13797     {
13798         // cleans up the whole document..
13799         Roo.log('cleanuppaste');
13800         
13801         this.cleanUpChildren(this.doc.body);
13802         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13803         if (clean != this.doc.body.innerHTML) {
13804             this.doc.body.innerHTML = clean;
13805         }
13806         
13807     },
13808     
13809     cleanWordChars : function(input) {// change the chars to hex code
13810         var he = Roo.HtmlEditorCore;
13811         
13812         var output = input;
13813         Roo.each(he.swapCodes, function(sw) { 
13814             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13815             
13816             output = output.replace(swapper, sw[1]);
13817         });
13818         
13819         return output;
13820     },
13821     
13822     
13823     cleanUpChildren : function (n)
13824     {
13825         if (!n.childNodes.length) {
13826             return;
13827         }
13828         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13829            this.cleanUpChild(n.childNodes[i]);
13830         }
13831     },
13832     
13833     
13834         
13835     
13836     cleanUpChild : function (node)
13837     {
13838         var ed = this;
13839         //console.log(node);
13840         if (node.nodeName == "#text") {
13841             // clean up silly Windows -- stuff?
13842             return; 
13843         }
13844         if (node.nodeName == "#comment") {
13845             node.parentNode.removeChild(node);
13846             // clean up silly Windows -- stuff?
13847             return; 
13848         }
13849         
13850         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13851             // remove node.
13852             node.parentNode.removeChild(node);
13853             return;
13854             
13855         }
13856         
13857         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13858         
13859         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13860         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13861         
13862         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13863         //    remove_keep_children = true;
13864         //}
13865         
13866         if (remove_keep_children) {
13867             this.cleanUpChildren(node);
13868             // inserts everything just before this node...
13869             while (node.childNodes.length) {
13870                 var cn = node.childNodes[0];
13871                 node.removeChild(cn);
13872                 node.parentNode.insertBefore(cn, node);
13873             }
13874             node.parentNode.removeChild(node);
13875             return;
13876         }
13877         
13878         if (!node.attributes || !node.attributes.length) {
13879             this.cleanUpChildren(node);
13880             return;
13881         }
13882         
13883         function cleanAttr(n,v)
13884         {
13885             
13886             if (v.match(/^\./) || v.match(/^\//)) {
13887                 return;
13888             }
13889             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13890                 return;
13891             }
13892             if (v.match(/^#/)) {
13893                 return;
13894             }
13895 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13896             node.removeAttribute(n);
13897             
13898         }
13899         
13900         function cleanStyle(n,v)
13901         {
13902             if (v.match(/expression/)) { //XSS?? should we even bother..
13903                 node.removeAttribute(n);
13904                 return;
13905             }
13906             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13907             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13908             
13909             
13910             var parts = v.split(/;/);
13911             var clean = [];
13912             
13913             Roo.each(parts, function(p) {
13914                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13915                 if (!p.length) {
13916                     return true;
13917                 }
13918                 var l = p.split(':').shift().replace(/\s+/g,'');
13919                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13920                 
13921                 if ( cblack.indexOf(l) > -1) {
13922 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13923                     //node.removeAttribute(n);
13924                     return true;
13925                 }
13926                 //Roo.log()
13927                 // only allow 'c whitelisted system attributes'
13928                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13929 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13930                     //node.removeAttribute(n);
13931                     return true;
13932                 }
13933                 
13934                 
13935                  
13936                 
13937                 clean.push(p);
13938                 return true;
13939             });
13940             if (clean.length) { 
13941                 node.setAttribute(n, clean.join(';'));
13942             } else {
13943                 node.removeAttribute(n);
13944             }
13945             
13946         }
13947         
13948         
13949         for (var i = node.attributes.length-1; i > -1 ; i--) {
13950             var a = node.attributes[i];
13951             //console.log(a);
13952             
13953             if (a.name.toLowerCase().substr(0,2)=='on')  {
13954                 node.removeAttribute(a.name);
13955                 continue;
13956             }
13957             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13958                 node.removeAttribute(a.name);
13959                 continue;
13960             }
13961             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13962                 cleanAttr(a.name,a.value); // fixme..
13963                 continue;
13964             }
13965             if (a.name == 'style') {
13966                 cleanStyle(a.name,a.value);
13967                 continue;
13968             }
13969             /// clean up MS crap..
13970             // tecnically this should be a list of valid class'es..
13971             
13972             
13973             if (a.name == 'class') {
13974                 if (a.value.match(/^Mso/)) {
13975                     node.className = '';
13976                 }
13977                 
13978                 if (a.value.match(/body/)) {
13979                     node.className = '';
13980                 }
13981                 continue;
13982             }
13983             
13984             // style cleanup!?
13985             // class cleanup?
13986             
13987         }
13988         
13989         
13990         this.cleanUpChildren(node);
13991         
13992         
13993     }
13994     
13995     
13996     // hide stuff that is not compatible
13997     /**
13998      * @event blur
13999      * @hide
14000      */
14001     /**
14002      * @event change
14003      * @hide
14004      */
14005     /**
14006      * @event focus
14007      * @hide
14008      */
14009     /**
14010      * @event specialkey
14011      * @hide
14012      */
14013     /**
14014      * @cfg {String} fieldClass @hide
14015      */
14016     /**
14017      * @cfg {String} focusClass @hide
14018      */
14019     /**
14020      * @cfg {String} autoCreate @hide
14021      */
14022     /**
14023      * @cfg {String} inputType @hide
14024      */
14025     /**
14026      * @cfg {String} invalidClass @hide
14027      */
14028     /**
14029      * @cfg {String} invalidText @hide
14030      */
14031     /**
14032      * @cfg {String} msgFx @hide
14033      */
14034     /**
14035      * @cfg {String} validateOnBlur @hide
14036      */
14037 });
14038
14039 Roo.HtmlEditorCore.white = [
14040         'area', 'br', 'img', 'input', 'hr', 'wbr',
14041         
14042        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14043        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14044        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14045        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14046        'table',   'ul',         'xmp', 
14047        
14048        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14049       'thead',   'tr', 
14050      
14051       'dir', 'menu', 'ol', 'ul', 'dl',
14052        
14053       'embed',  'object'
14054 ];
14055
14056
14057 Roo.HtmlEditorCore.black = [
14058     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14059         'applet', // 
14060         'base',   'basefont', 'bgsound', 'blink',  'body', 
14061         'frame',  'frameset', 'head',    'html',   'ilayer', 
14062         'iframe', 'layer',  'link',     'meta',    'object',   
14063         'script', 'style' ,'title',  'xml' // clean later..
14064 ];
14065 Roo.HtmlEditorCore.clean = [
14066     'script', 'style', 'title', 'xml'
14067 ];
14068 Roo.HtmlEditorCore.remove = [
14069     'font'
14070 ];
14071 // attributes..
14072
14073 Roo.HtmlEditorCore.ablack = [
14074     'on'
14075 ];
14076     
14077 Roo.HtmlEditorCore.aclean = [ 
14078     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14079 ];
14080
14081 // protocols..
14082 Roo.HtmlEditorCore.pwhite= [
14083         'http',  'https',  'mailto'
14084 ];
14085
14086 // white listed style attributes.
14087 Roo.HtmlEditorCore.cwhite= [
14088       //  'text-align', /// default is to allow most things..
14089       
14090          
14091 //        'font-size'//??
14092 ];
14093
14094 // black listed style attributes.
14095 Roo.HtmlEditorCore.cblack= [
14096       //  'font-size' -- this can be set by the project 
14097 ];
14098
14099
14100 Roo.HtmlEditorCore.swapCodes   =[ 
14101     [    8211, "--" ], 
14102     [    8212, "--" ], 
14103     [    8216,  "'" ],  
14104     [    8217, "'" ],  
14105     [    8220, '"' ],  
14106     [    8221, '"' ],  
14107     [    8226, "*" ],  
14108     [    8230, "..." ]
14109 ]; 
14110
14111     /*
14112  * - LGPL
14113  *
14114  * HtmlEditor
14115  * 
14116  */
14117
14118 /**
14119  * @class Roo.bootstrap.HtmlEditor
14120  * @extends Roo.bootstrap.TextArea
14121  * Bootstrap HtmlEditor class
14122
14123  * @constructor
14124  * Create a new HtmlEditor
14125  * @param {Object} config The config object
14126  */
14127
14128 Roo.bootstrap.HtmlEditor = function(config){
14129     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14130     if (!this.toolbars) {
14131         this.toolbars = [];
14132     }
14133     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14134     this.addEvents({
14135             /**
14136              * @event initialize
14137              * Fires when the editor is fully initialized (including the iframe)
14138              * @param {HtmlEditor} this
14139              */
14140             initialize: true,
14141             /**
14142              * @event activate
14143              * Fires when the editor is first receives the focus. Any insertion must wait
14144              * until after this event.
14145              * @param {HtmlEditor} this
14146              */
14147             activate: true,
14148              /**
14149              * @event beforesync
14150              * Fires before the textarea is updated with content from the editor iframe. Return false
14151              * to cancel the sync.
14152              * @param {HtmlEditor} this
14153              * @param {String} html
14154              */
14155             beforesync: true,
14156              /**
14157              * @event beforepush
14158              * Fires before the iframe editor is updated with content from the textarea. Return false
14159              * to cancel the push.
14160              * @param {HtmlEditor} this
14161              * @param {String} html
14162              */
14163             beforepush: true,
14164              /**
14165              * @event sync
14166              * Fires when the textarea is updated with content from the editor iframe.
14167              * @param {HtmlEditor} this
14168              * @param {String} html
14169              */
14170             sync: true,
14171              /**
14172              * @event push
14173              * Fires when the iframe editor is updated with content from the textarea.
14174              * @param {HtmlEditor} this
14175              * @param {String} html
14176              */
14177             push: true,
14178              /**
14179              * @event editmodechange
14180              * Fires when the editor switches edit modes
14181              * @param {HtmlEditor} this
14182              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14183              */
14184             editmodechange: true,
14185             /**
14186              * @event editorevent
14187              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14188              * @param {HtmlEditor} this
14189              */
14190             editorevent: true,
14191             /**
14192              * @event firstfocus
14193              * Fires when on first focus - needed by toolbars..
14194              * @param {HtmlEditor} this
14195              */
14196             firstfocus: true,
14197             /**
14198              * @event autosave
14199              * Auto save the htmlEditor value as a file into Events
14200              * @param {HtmlEditor} this
14201              */
14202             autosave: true,
14203             /**
14204              * @event savedpreview
14205              * preview the saved version of htmlEditor
14206              * @param {HtmlEditor} this
14207              */
14208             savedpreview: true
14209         });
14210 };
14211
14212
14213 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14214     
14215     
14216       /**
14217      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14218      */
14219     toolbars : false,
14220    
14221      /**
14222      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14223      *                        Roo.resizable.
14224      */
14225     resizable : false,
14226      /**
14227      * @cfg {Number} height (in pixels)
14228      */   
14229     height: 300,
14230    /**
14231      * @cfg {Number} width (in pixels)
14232      */   
14233     width: false,
14234     
14235     /**
14236      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14237      * 
14238      */
14239     stylesheets: false,
14240     
14241     // id of frame..
14242     frameId: false,
14243     
14244     // private properties
14245     validationEvent : false,
14246     deferHeight: true,
14247     initialized : false,
14248     activated : false,
14249     
14250     onFocus : Roo.emptyFn,
14251     iframePad:3,
14252     hideMode:'offsets',
14253     
14254     
14255     tbContainer : false,
14256     
14257     toolbarContainer :function() {
14258         return this.wrap.select('.x-html-editor-tb',true).first();
14259     },
14260
14261     /**
14262      * Protected method that will not generally be called directly. It
14263      * is called when the editor creates its toolbar. Override this method if you need to
14264      * add custom toolbar buttons.
14265      * @param {HtmlEditor} editor
14266      */
14267     createToolbar : function(){
14268         
14269         Roo.log("create toolbars");
14270         
14271         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14272         this.toolbars[0].render(this.toolbarContainer());
14273         
14274         return;
14275         
14276 //        if (!editor.toolbars || !editor.toolbars.length) {
14277 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14278 //        }
14279 //        
14280 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14281 //            editor.toolbars[i] = Roo.factory(
14282 //                    typeof(editor.toolbars[i]) == 'string' ?
14283 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14284 //                Roo.bootstrap.HtmlEditor);
14285 //            editor.toolbars[i].init(editor);
14286 //        }
14287     },
14288
14289      
14290     // private
14291     onRender : function(ct, position)
14292     {
14293        // Roo.log("Call onRender: " + this.xtype);
14294         var _t = this;
14295         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14296       
14297         this.wrap = this.inputEl().wrap({
14298             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14299         });
14300         
14301         this.editorcore.onRender(ct, position);
14302          
14303         if (this.resizable) {
14304             this.resizeEl = new Roo.Resizable(this.wrap, {
14305                 pinned : true,
14306                 wrap: true,
14307                 dynamic : true,
14308                 minHeight : this.height,
14309                 height: this.height,
14310                 handles : this.resizable,
14311                 width: this.width,
14312                 listeners : {
14313                     resize : function(r, w, h) {
14314                         _t.onResize(w,h); // -something
14315                     }
14316                 }
14317             });
14318             
14319         }
14320         this.createToolbar(this);
14321        
14322         
14323         if(!this.width && this.resizable){
14324             this.setSize(this.wrap.getSize());
14325         }
14326         if (this.resizeEl) {
14327             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14328             // should trigger onReize..
14329         }
14330         
14331     },
14332
14333     // private
14334     onResize : function(w, h)
14335     {
14336         Roo.log('resize: ' +w + ',' + h );
14337         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14338         var ew = false;
14339         var eh = false;
14340         
14341         if(this.inputEl() ){
14342             if(typeof w == 'number'){
14343                 var aw = w - this.wrap.getFrameWidth('lr');
14344                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14345                 ew = aw;
14346             }
14347             if(typeof h == 'number'){
14348                  var tbh = -11;  // fixme it needs to tool bar size!
14349                 for (var i =0; i < this.toolbars.length;i++) {
14350                     // fixme - ask toolbars for heights?
14351                     tbh += this.toolbars[i].el.getHeight();
14352                     //if (this.toolbars[i].footer) {
14353                     //    tbh += this.toolbars[i].footer.el.getHeight();
14354                     //}
14355                 }
14356               
14357                 
14358                 
14359                 
14360                 
14361                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14362                 ah -= 5; // knock a few pixes off for look..
14363                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14364                 var eh = ah;
14365             }
14366         }
14367         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14368         this.editorcore.onResize(ew,eh);
14369         
14370     },
14371
14372     /**
14373      * Toggles the editor between standard and source edit mode.
14374      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14375      */
14376     toggleSourceEdit : function(sourceEditMode)
14377     {
14378         this.editorcore.toggleSourceEdit(sourceEditMode);
14379         
14380         if(this.editorcore.sourceEditMode){
14381             Roo.log('editor - showing textarea');
14382             
14383 //            Roo.log('in');
14384 //            Roo.log(this.syncValue());
14385             this.syncValue();
14386             this.inputEl().removeClass('hide');
14387             this.inputEl().dom.removeAttribute('tabIndex');
14388             this.inputEl().focus();
14389         }else{
14390             Roo.log('editor - hiding textarea');
14391 //            Roo.log('out')
14392 //            Roo.log(this.pushValue()); 
14393             this.pushValue();
14394             
14395             this.inputEl().addClass('hide');
14396             this.inputEl().dom.setAttribute('tabIndex', -1);
14397             //this.deferFocus();
14398         }
14399          
14400         if(this.resizable){
14401             this.setSize(this.wrap.getSize());
14402         }
14403         
14404         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14405     },
14406  
14407     // private (for BoxComponent)
14408     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14409
14410     // private (for BoxComponent)
14411     getResizeEl : function(){
14412         return this.wrap;
14413     },
14414
14415     // private (for BoxComponent)
14416     getPositionEl : function(){
14417         return this.wrap;
14418     },
14419
14420     // private
14421     initEvents : function(){
14422         this.originalValue = this.getValue();
14423     },
14424
14425 //    /**
14426 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14427 //     * @method
14428 //     */
14429 //    markInvalid : Roo.emptyFn,
14430 //    /**
14431 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14432 //     * @method
14433 //     */
14434 //    clearInvalid : Roo.emptyFn,
14435
14436     setValue : function(v){
14437         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14438         this.editorcore.pushValue();
14439     },
14440
14441      
14442     // private
14443     deferFocus : function(){
14444         this.focus.defer(10, this);
14445     },
14446
14447     // doc'ed in Field
14448     focus : function(){
14449         this.editorcore.focus();
14450         
14451     },
14452       
14453
14454     // private
14455     onDestroy : function(){
14456         
14457         
14458         
14459         if(this.rendered){
14460             
14461             for (var i =0; i < this.toolbars.length;i++) {
14462                 // fixme - ask toolbars for heights?
14463                 this.toolbars[i].onDestroy();
14464             }
14465             
14466             this.wrap.dom.innerHTML = '';
14467             this.wrap.remove();
14468         }
14469     },
14470
14471     // private
14472     onFirstFocus : function(){
14473         //Roo.log("onFirstFocus");
14474         this.editorcore.onFirstFocus();
14475          for (var i =0; i < this.toolbars.length;i++) {
14476             this.toolbars[i].onFirstFocus();
14477         }
14478         
14479     },
14480     
14481     // private
14482     syncValue : function()
14483     {   
14484         this.editorcore.syncValue();
14485     },
14486     
14487     pushValue : function()
14488     {   
14489         this.editorcore.pushValue();
14490     }
14491      
14492     
14493     // hide stuff that is not compatible
14494     /**
14495      * @event blur
14496      * @hide
14497      */
14498     /**
14499      * @event change
14500      * @hide
14501      */
14502     /**
14503      * @event focus
14504      * @hide
14505      */
14506     /**
14507      * @event specialkey
14508      * @hide
14509      */
14510     /**
14511      * @cfg {String} fieldClass @hide
14512      */
14513     /**
14514      * @cfg {String} focusClass @hide
14515      */
14516     /**
14517      * @cfg {String} autoCreate @hide
14518      */
14519     /**
14520      * @cfg {String} inputType @hide
14521      */
14522     /**
14523      * @cfg {String} invalidClass @hide
14524      */
14525     /**
14526      * @cfg {String} invalidText @hide
14527      */
14528     /**
14529      * @cfg {String} msgFx @hide
14530      */
14531     /**
14532      * @cfg {String} validateOnBlur @hide
14533      */
14534 });
14535  
14536     
14537    
14538    
14539    
14540       
14541
14542 /**
14543  * @class Roo.bootstrap.HtmlEditorToolbar1
14544  * Basic Toolbar
14545  * 
14546  * Usage:
14547  *
14548  new Roo.bootstrap.HtmlEditor({
14549     ....
14550     toolbars : [
14551         new Roo.bootstrap.HtmlEditorToolbar1({
14552             disable : { fonts: 1 , format: 1, ..., ... , ...],
14553             btns : [ .... ]
14554         })
14555     }
14556      
14557  * 
14558  * @cfg {Object} disable List of elements to disable..
14559  * @cfg {Array} btns List of additional buttons.
14560  * 
14561  * 
14562  * NEEDS Extra CSS? 
14563  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14564  */
14565  
14566 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14567 {
14568     
14569     Roo.apply(this, config);
14570     
14571     // default disabled, based on 'good practice'..
14572     this.disable = this.disable || {};
14573     Roo.applyIf(this.disable, {
14574         fontSize : true,
14575         colors : true,
14576         specialElements : true
14577     });
14578     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14579     
14580     this.editor = config.editor;
14581     this.editorcore = config.editor.editorcore;
14582     
14583     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14584     
14585     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14586     // dont call parent... till later.
14587 }
14588 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14589     
14590     
14591     bar : true,
14592     
14593     editor : false,
14594     editorcore : false,
14595     
14596     
14597     formats : [
14598         "p" ,  
14599         "h1","h2","h3","h4","h5","h6", 
14600         "pre", "code", 
14601         "abbr", "acronym", "address", "cite", "samp", "var",
14602         'div','span'
14603     ],
14604     
14605     onRender : function(ct, position)
14606     {
14607        // Roo.log("Call onRender: " + this.xtype);
14608         
14609        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14610        Roo.log(this.el);
14611        this.el.dom.style.marginBottom = '0';
14612        var _this = this;
14613        var editorcore = this.editorcore;
14614        var editor= this.editor;
14615        
14616        var children = [];
14617        var btn = function(id,cmd , toggle, handler){
14618        
14619             var  event = toggle ? 'toggle' : 'click';
14620        
14621             var a = {
14622                 size : 'sm',
14623                 xtype: 'Button',
14624                 xns: Roo.bootstrap,
14625                 glyphicon : id,
14626                 cmd : id || cmd,
14627                 enableToggle:toggle !== false,
14628                 //html : 'submit'
14629                 pressed : toggle ? false : null,
14630                 listeners : {}
14631             }
14632             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14633                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14634             }
14635             children.push(a);
14636             return a;
14637        }
14638         
14639         var style = {
14640                 xtype: 'Button',
14641                 size : 'sm',
14642                 xns: Roo.bootstrap,
14643                 glyphicon : 'font',
14644                 //html : 'submit'
14645                 menu : {
14646                     xtype: 'Menu',
14647                     xns: Roo.bootstrap,
14648                     items:  []
14649                 }
14650         };
14651         Roo.each(this.formats, function(f) {
14652             style.menu.items.push({
14653                 xtype :'MenuItem',
14654                 xns: Roo.bootstrap,
14655                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14656                 tagname : f,
14657                 listeners : {
14658                     click : function()
14659                     {
14660                         editorcore.insertTag(this.tagname);
14661                         editor.focus();
14662                     }
14663                 }
14664                 
14665             });
14666         });
14667          children.push(style);   
14668             
14669             
14670         btn('bold',false,true);
14671         btn('italic',false,true);
14672         btn('align-left', 'justifyleft',true);
14673         btn('align-center', 'justifycenter',true);
14674         btn('align-right' , 'justifyright',true);
14675         btn('link', false, false, function(btn) {
14676             //Roo.log("create link?");
14677             var url = prompt(this.createLinkText, this.defaultLinkValue);
14678             if(url && url != 'http:/'+'/'){
14679                 this.editorcore.relayCmd('createlink', url);
14680             }
14681         }),
14682         btn('list','insertunorderedlist',true);
14683         btn('pencil', false,true, function(btn){
14684                 Roo.log(this);
14685                 
14686                 this.toggleSourceEdit(btn.pressed);
14687         });
14688         /*
14689         var cog = {
14690                 xtype: 'Button',
14691                 size : 'sm',
14692                 xns: Roo.bootstrap,
14693                 glyphicon : 'cog',
14694                 //html : 'submit'
14695                 menu : {
14696                     xtype: 'Menu',
14697                     xns: Roo.bootstrap,
14698                     items:  []
14699                 }
14700         };
14701         
14702         cog.menu.items.push({
14703             xtype :'MenuItem',
14704             xns: Roo.bootstrap,
14705             html : Clean styles,
14706             tagname : f,
14707             listeners : {
14708                 click : function()
14709                 {
14710                     editorcore.insertTag(this.tagname);
14711                     editor.focus();
14712                 }
14713             }
14714             
14715         });
14716        */
14717         
14718          
14719        this.xtype = 'Navbar';
14720         
14721         for(var i=0;i< children.length;i++) {
14722             
14723             this.buttons.add(this.addxtypeChild(children[i]));
14724             
14725         }
14726         
14727         editor.on('editorevent', this.updateToolbar, this);
14728     },
14729     onBtnClick : function(id)
14730     {
14731        this.editorcore.relayCmd(id);
14732        this.editorcore.focus();
14733     },
14734     
14735     /**
14736      * Protected method that will not generally be called directly. It triggers
14737      * a toolbar update by reading the markup state of the current selection in the editor.
14738      */
14739     updateToolbar: function(){
14740
14741         if(!this.editorcore.activated){
14742             this.editor.onFirstFocus(); // is this neeed?
14743             return;
14744         }
14745
14746         var btns = this.buttons; 
14747         var doc = this.editorcore.doc;
14748         btns.get('bold').setActive(doc.queryCommandState('bold'));
14749         btns.get('italic').setActive(doc.queryCommandState('italic'));
14750         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14751         
14752         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14753         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14754         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14755         
14756         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14757         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14758          /*
14759         
14760         var ans = this.editorcore.getAllAncestors();
14761         if (this.formatCombo) {
14762             
14763             
14764             var store = this.formatCombo.store;
14765             this.formatCombo.setValue("");
14766             for (var i =0; i < ans.length;i++) {
14767                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14768                     // select it..
14769                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14770                     break;
14771                 }
14772             }
14773         }
14774         
14775         
14776         
14777         // hides menus... - so this cant be on a menu...
14778         Roo.bootstrap.MenuMgr.hideAll();
14779         */
14780         Roo.bootstrap.MenuMgr.hideAll();
14781         //this.editorsyncValue();
14782     },
14783     onFirstFocus: function() {
14784         this.buttons.each(function(item){
14785            item.enable();
14786         });
14787     },
14788     toggleSourceEdit : function(sourceEditMode){
14789         
14790           
14791         if(sourceEditMode){
14792             Roo.log("disabling buttons");
14793            this.buttons.each( function(item){
14794                 if(item.cmd != 'pencil'){
14795                     item.disable();
14796                 }
14797             });
14798           
14799         }else{
14800             Roo.log("enabling buttons");
14801             if(this.editorcore.initialized){
14802                 this.buttons.each( function(item){
14803                     item.enable();
14804                 });
14805             }
14806             
14807         }
14808         Roo.log("calling toggole on editor");
14809         // tell the editor that it's been pressed..
14810         this.editor.toggleSourceEdit(sourceEditMode);
14811        
14812     }
14813 });
14814
14815
14816
14817
14818
14819 /**
14820  * @class Roo.bootstrap.Table.AbstractSelectionModel
14821  * @extends Roo.util.Observable
14822  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14823  * implemented by descendant classes.  This class should not be directly instantiated.
14824  * @constructor
14825  */
14826 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14827     this.locked = false;
14828     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14829 };
14830
14831
14832 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14833     /** @ignore Called by the grid automatically. Do not call directly. */
14834     init : function(grid){
14835         this.grid = grid;
14836         this.initEvents();
14837     },
14838
14839     /**
14840      * Locks the selections.
14841      */
14842     lock : function(){
14843         this.locked = true;
14844     },
14845
14846     /**
14847      * Unlocks the selections.
14848      */
14849     unlock : function(){
14850         this.locked = false;
14851     },
14852
14853     /**
14854      * Returns true if the selections are locked.
14855      * @return {Boolean}
14856      */
14857     isLocked : function(){
14858         return this.locked;
14859     }
14860 });
14861 /**
14862  * @class Roo.bootstrap.Table.ColumnModel
14863  * @extends Roo.util.Observable
14864  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14865  * the columns in the table.
14866  
14867  * @constructor
14868  * @param {Object} config An Array of column config objects. See this class's
14869  * config objects for details.
14870 */
14871 Roo.bootstrap.Table.ColumnModel = function(config){
14872         /**
14873      * The config passed into the constructor
14874      */
14875     this.config = config;
14876     this.lookup = {};
14877
14878     // if no id, create one
14879     // if the column does not have a dataIndex mapping,
14880     // map it to the order it is in the config
14881     for(var i = 0, len = config.length; i < len; i++){
14882         var c = config[i];
14883         if(typeof c.dataIndex == "undefined"){
14884             c.dataIndex = i;
14885         }
14886         if(typeof c.renderer == "string"){
14887             c.renderer = Roo.util.Format[c.renderer];
14888         }
14889         if(typeof c.id == "undefined"){
14890             c.id = Roo.id();
14891         }
14892 //        if(c.editor && c.editor.xtype){
14893 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14894 //        }
14895 //        if(c.editor && c.editor.isFormField){
14896 //            c.editor = new Roo.grid.GridEditor(c.editor);
14897 //        }
14898
14899         this.lookup[c.id] = c;
14900     }
14901
14902     /**
14903      * The width of columns which have no width specified (defaults to 100)
14904      * @type Number
14905      */
14906     this.defaultWidth = 100;
14907
14908     /**
14909      * Default sortable of columns which have no sortable specified (defaults to false)
14910      * @type Boolean
14911      */
14912     this.defaultSortable = false;
14913
14914     this.addEvents({
14915         /**
14916              * @event widthchange
14917              * Fires when the width of a column changes.
14918              * @param {ColumnModel} this
14919              * @param {Number} columnIndex The column index
14920              * @param {Number} newWidth The new width
14921              */
14922             "widthchange": true,
14923         /**
14924              * @event headerchange
14925              * Fires when the text of a header changes.
14926              * @param {ColumnModel} this
14927              * @param {Number} columnIndex The column index
14928              * @param {Number} newText The new header text
14929              */
14930             "headerchange": true,
14931         /**
14932              * @event hiddenchange
14933              * Fires when a column is hidden or "unhidden".
14934              * @param {ColumnModel} this
14935              * @param {Number} columnIndex The column index
14936              * @param {Boolean} hidden true if hidden, false otherwise
14937              */
14938             "hiddenchange": true,
14939             /**
14940          * @event columnmoved
14941          * Fires when a column is moved.
14942          * @param {ColumnModel} this
14943          * @param {Number} oldIndex
14944          * @param {Number} newIndex
14945          */
14946         "columnmoved" : true,
14947         /**
14948          * @event columlockchange
14949          * Fires when a column's locked state is changed
14950          * @param {ColumnModel} this
14951          * @param {Number} colIndex
14952          * @param {Boolean} locked true if locked
14953          */
14954         "columnlockchange" : true
14955     });
14956     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14957 };
14958 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14959     /**
14960      * @cfg {String} header The header text to display in the Grid view.
14961      */
14962     /**
14963      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14964      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14965      * specified, the column's index is used as an index into the Record's data Array.
14966      */
14967     /**
14968      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14969      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14970      */
14971     /**
14972      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14973      * Defaults to the value of the {@link #defaultSortable} property.
14974      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14975      */
14976     /**
14977      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14978      */
14979     /**
14980      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14981      */
14982     /**
14983      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14984      */
14985     /**
14986      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14987      */
14988     /**
14989      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14990      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14991      * default renderer uses the raw data value.
14992      */
14993     /**
14994      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14995      */
14996
14997     /**
14998      * Returns the id of the column at the specified index.
14999      * @param {Number} index The column index
15000      * @return {String} the id
15001      */
15002     getColumnId : function(index){
15003         return this.config[index].id;
15004     },
15005
15006     /**
15007      * Returns the column for a specified id.
15008      * @param {String} id The column id
15009      * @return {Object} the column
15010      */
15011     getColumnById : function(id){
15012         return this.lookup[id];
15013     },
15014
15015     
15016     /**
15017      * Returns the column for a specified dataIndex.
15018      * @param {String} dataIndex The column dataIndex
15019      * @return {Object|Boolean} the column or false if not found
15020      */
15021     getColumnByDataIndex: function(dataIndex){
15022         var index = this.findColumnIndex(dataIndex);
15023         return index > -1 ? this.config[index] : false;
15024     },
15025     
15026     /**
15027      * Returns the index for a specified column id.
15028      * @param {String} id The column id
15029      * @return {Number} the index, or -1 if not found
15030      */
15031     getIndexById : function(id){
15032         for(var i = 0, len = this.config.length; i < len; i++){
15033             if(this.config[i].id == id){
15034                 return i;
15035             }
15036         }
15037         return -1;
15038     },
15039     
15040     /**
15041      * Returns the index for a specified column dataIndex.
15042      * @param {String} dataIndex The column dataIndex
15043      * @return {Number} the index, or -1 if not found
15044      */
15045     
15046     findColumnIndex : function(dataIndex){
15047         for(var i = 0, len = this.config.length; i < len; i++){
15048             if(this.config[i].dataIndex == dataIndex){
15049                 return i;
15050             }
15051         }
15052         return -1;
15053     },
15054     
15055     
15056     moveColumn : function(oldIndex, newIndex){
15057         var c = this.config[oldIndex];
15058         this.config.splice(oldIndex, 1);
15059         this.config.splice(newIndex, 0, c);
15060         this.dataMap = null;
15061         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15062     },
15063
15064     isLocked : function(colIndex){
15065         return this.config[colIndex].locked === true;
15066     },
15067
15068     setLocked : function(colIndex, value, suppressEvent){
15069         if(this.isLocked(colIndex) == value){
15070             return;
15071         }
15072         this.config[colIndex].locked = value;
15073         if(!suppressEvent){
15074             this.fireEvent("columnlockchange", this, colIndex, value);
15075         }
15076     },
15077
15078     getTotalLockedWidth : function(){
15079         var totalWidth = 0;
15080         for(var i = 0; i < this.config.length; i++){
15081             if(this.isLocked(i) && !this.isHidden(i)){
15082                 this.totalWidth += this.getColumnWidth(i);
15083             }
15084         }
15085         return totalWidth;
15086     },
15087
15088     getLockedCount : function(){
15089         for(var i = 0, len = this.config.length; i < len; i++){
15090             if(!this.isLocked(i)){
15091                 return i;
15092             }
15093         }
15094     },
15095
15096     /**
15097      * Returns the number of columns.
15098      * @return {Number}
15099      */
15100     getColumnCount : function(visibleOnly){
15101         if(visibleOnly === true){
15102             var c = 0;
15103             for(var i = 0, len = this.config.length; i < len; i++){
15104                 if(!this.isHidden(i)){
15105                     c++;
15106                 }
15107             }
15108             return c;
15109         }
15110         return this.config.length;
15111     },
15112
15113     /**
15114      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15115      * @param {Function} fn
15116      * @param {Object} scope (optional)
15117      * @return {Array} result
15118      */
15119     getColumnsBy : function(fn, scope){
15120         var r = [];
15121         for(var i = 0, len = this.config.length; i < len; i++){
15122             var c = this.config[i];
15123             if(fn.call(scope||this, c, i) === true){
15124                 r[r.length] = c;
15125             }
15126         }
15127         return r;
15128     },
15129
15130     /**
15131      * Returns true if the specified column is sortable.
15132      * @param {Number} col The column index
15133      * @return {Boolean}
15134      */
15135     isSortable : function(col){
15136         if(typeof this.config[col].sortable == "undefined"){
15137             return this.defaultSortable;
15138         }
15139         return this.config[col].sortable;
15140     },
15141
15142     /**
15143      * Returns the rendering (formatting) function defined for the column.
15144      * @param {Number} col The column index.
15145      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15146      */
15147     getRenderer : function(col){
15148         if(!this.config[col].renderer){
15149             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15150         }
15151         return this.config[col].renderer;
15152     },
15153
15154     /**
15155      * Sets the rendering (formatting) function for a column.
15156      * @param {Number} col The column index
15157      * @param {Function} fn The function to use to process the cell's raw data
15158      * to return HTML markup for the grid view. The render function is called with
15159      * the following parameters:<ul>
15160      * <li>Data value.</li>
15161      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15162      * <li>css A CSS style string to apply to the table cell.</li>
15163      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15164      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15165      * <li>Row index</li>
15166      * <li>Column index</li>
15167      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15168      */
15169     setRenderer : function(col, fn){
15170         this.config[col].renderer = fn;
15171     },
15172
15173     /**
15174      * Returns the width for the specified column.
15175      * @param {Number} col The column index
15176      * @return {Number}
15177      */
15178     getColumnWidth : function(col){
15179         return this.config[col].width * 1 || this.defaultWidth;
15180     },
15181
15182     /**
15183      * Sets the width for a column.
15184      * @param {Number} col The column index
15185      * @param {Number} width The new width
15186      */
15187     setColumnWidth : function(col, width, suppressEvent){
15188         this.config[col].width = width;
15189         this.totalWidth = null;
15190         if(!suppressEvent){
15191              this.fireEvent("widthchange", this, col, width);
15192         }
15193     },
15194
15195     /**
15196      * Returns the total width of all columns.
15197      * @param {Boolean} includeHidden True to include hidden column widths
15198      * @return {Number}
15199      */
15200     getTotalWidth : function(includeHidden){
15201         if(!this.totalWidth){
15202             this.totalWidth = 0;
15203             for(var i = 0, len = this.config.length; i < len; i++){
15204                 if(includeHidden || !this.isHidden(i)){
15205                     this.totalWidth += this.getColumnWidth(i);
15206                 }
15207             }
15208         }
15209         return this.totalWidth;
15210     },
15211
15212     /**
15213      * Returns the header for the specified column.
15214      * @param {Number} col The column index
15215      * @return {String}
15216      */
15217     getColumnHeader : function(col){
15218         return this.config[col].header;
15219     },
15220
15221     /**
15222      * Sets the header for a column.
15223      * @param {Number} col The column index
15224      * @param {String} header The new header
15225      */
15226     setColumnHeader : function(col, header){
15227         this.config[col].header = header;
15228         this.fireEvent("headerchange", this, col, header);
15229     },
15230
15231     /**
15232      * Returns the tooltip for the specified column.
15233      * @param {Number} col The column index
15234      * @return {String}
15235      */
15236     getColumnTooltip : function(col){
15237             return this.config[col].tooltip;
15238     },
15239     /**
15240      * Sets the tooltip for a column.
15241      * @param {Number} col The column index
15242      * @param {String} tooltip The new tooltip
15243      */
15244     setColumnTooltip : function(col, tooltip){
15245             this.config[col].tooltip = tooltip;
15246     },
15247
15248     /**
15249      * Returns the dataIndex for the specified column.
15250      * @param {Number} col The column index
15251      * @return {Number}
15252      */
15253     getDataIndex : function(col){
15254         return this.config[col].dataIndex;
15255     },
15256
15257     /**
15258      * Sets the dataIndex for a column.
15259      * @param {Number} col The column index
15260      * @param {Number} dataIndex The new dataIndex
15261      */
15262     setDataIndex : function(col, dataIndex){
15263         this.config[col].dataIndex = dataIndex;
15264     },
15265
15266     
15267     
15268     /**
15269      * Returns true if the cell is editable.
15270      * @param {Number} colIndex The column index
15271      * @param {Number} rowIndex The row index
15272      * @return {Boolean}
15273      */
15274     isCellEditable : function(colIndex, rowIndex){
15275         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15276     },
15277
15278     /**
15279      * Returns the editor defined for the cell/column.
15280      * return false or null to disable editing.
15281      * @param {Number} colIndex The column index
15282      * @param {Number} rowIndex The row index
15283      * @return {Object}
15284      */
15285     getCellEditor : function(colIndex, rowIndex){
15286         return this.config[colIndex].editor;
15287     },
15288
15289     /**
15290      * Sets if a column is editable.
15291      * @param {Number} col The column index
15292      * @param {Boolean} editable True if the column is editable
15293      */
15294     setEditable : function(col, editable){
15295         this.config[col].editable = editable;
15296     },
15297
15298
15299     /**
15300      * Returns true if the column is hidden.
15301      * @param {Number} colIndex The column index
15302      * @return {Boolean}
15303      */
15304     isHidden : function(colIndex){
15305         return this.config[colIndex].hidden;
15306     },
15307
15308
15309     /**
15310      * Returns true if the column width cannot be changed
15311      */
15312     isFixed : function(colIndex){
15313         return this.config[colIndex].fixed;
15314     },
15315
15316     /**
15317      * Returns true if the column can be resized
15318      * @return {Boolean}
15319      */
15320     isResizable : function(colIndex){
15321         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15322     },
15323     /**
15324      * Sets if a column is hidden.
15325      * @param {Number} colIndex The column index
15326      * @param {Boolean} hidden True if the column is hidden
15327      */
15328     setHidden : function(colIndex, hidden){
15329         this.config[colIndex].hidden = hidden;
15330         this.totalWidth = null;
15331         this.fireEvent("hiddenchange", this, colIndex, hidden);
15332     },
15333
15334     /**
15335      * Sets the editor for a column.
15336      * @param {Number} col The column index
15337      * @param {Object} editor The editor object
15338      */
15339     setEditor : function(col, editor){
15340         this.config[col].editor = editor;
15341     }
15342 });
15343
15344 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15345         if(typeof value == "string" && value.length < 1){
15346             return "&#160;";
15347         }
15348         return value;
15349 };
15350
15351 // Alias for backwards compatibility
15352 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15353
15354 /**
15355  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15356  * @class Roo.bootstrap.Table.RowSelectionModel
15357  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15358  * It supports multiple selections and keyboard selection/navigation. 
15359  * @constructor
15360  * @param {Object} config
15361  */
15362
15363 Roo.bootstrap.Table.RowSelectionModel = function(config){
15364     Roo.apply(this, config);
15365     this.selections = new Roo.util.MixedCollection(false, function(o){
15366         return o.id;
15367     });
15368
15369     this.last = false;
15370     this.lastActive = false;
15371
15372     this.addEvents({
15373         /**
15374              * @event selectionchange
15375              * Fires when the selection changes
15376              * @param {SelectionModel} this
15377              */
15378             "selectionchange" : true,
15379         /**
15380              * @event afterselectionchange
15381              * Fires after the selection changes (eg. by key press or clicking)
15382              * @param {SelectionModel} this
15383              */
15384             "afterselectionchange" : true,
15385         /**
15386              * @event beforerowselect
15387              * Fires when a row is selected being selected, return false to cancel.
15388              * @param {SelectionModel} this
15389              * @param {Number} rowIndex The selected index
15390              * @param {Boolean} keepExisting False if other selections will be cleared
15391              */
15392             "beforerowselect" : true,
15393         /**
15394              * @event rowselect
15395              * Fires when a row is selected.
15396              * @param {SelectionModel} this
15397              * @param {Number} rowIndex The selected index
15398              * @param {Roo.data.Record} r The record
15399              */
15400             "rowselect" : true,
15401         /**
15402              * @event rowdeselect
15403              * Fires when a row is deselected.
15404              * @param {SelectionModel} this
15405              * @param {Number} rowIndex The selected index
15406              */
15407         "rowdeselect" : true
15408     });
15409     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15410     this.locked = false;
15411 };
15412
15413 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15414     /**
15415      * @cfg {Boolean} singleSelect
15416      * True to allow selection of only one row at a time (defaults to false)
15417      */
15418     singleSelect : false,
15419
15420     // private
15421     initEvents : function(){
15422
15423         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15424             this.grid.on("mousedown", this.handleMouseDown, this);
15425         }else{ // allow click to work like normal
15426             this.grid.on("rowclick", this.handleDragableRowClick, this);
15427         }
15428
15429         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15430             "up" : function(e){
15431                 if(!e.shiftKey){
15432                     this.selectPrevious(e.shiftKey);
15433                 }else if(this.last !== false && this.lastActive !== false){
15434                     var last = this.last;
15435                     this.selectRange(this.last,  this.lastActive-1);
15436                     this.grid.getView().focusRow(this.lastActive);
15437                     if(last !== false){
15438                         this.last = last;
15439                     }
15440                 }else{
15441                     this.selectFirstRow();
15442                 }
15443                 this.fireEvent("afterselectionchange", this);
15444             },
15445             "down" : function(e){
15446                 if(!e.shiftKey){
15447                     this.selectNext(e.shiftKey);
15448                 }else if(this.last !== false && this.lastActive !== false){
15449                     var last = this.last;
15450                     this.selectRange(this.last,  this.lastActive+1);
15451                     this.grid.getView().focusRow(this.lastActive);
15452                     if(last !== false){
15453                         this.last = last;
15454                     }
15455                 }else{
15456                     this.selectFirstRow();
15457                 }
15458                 this.fireEvent("afterselectionchange", this);
15459             },
15460             scope: this
15461         });
15462
15463         var view = this.grid.view;
15464         view.on("refresh", this.onRefresh, this);
15465         view.on("rowupdated", this.onRowUpdated, this);
15466         view.on("rowremoved", this.onRemove, this);
15467     },
15468
15469     // private
15470     onRefresh : function(){
15471         var ds = this.grid.dataSource, i, v = this.grid.view;
15472         var s = this.selections;
15473         s.each(function(r){
15474             if((i = ds.indexOfId(r.id)) != -1){
15475                 v.onRowSelect(i);
15476             }else{
15477                 s.remove(r);
15478             }
15479         });
15480     },
15481
15482     // private
15483     onRemove : function(v, index, r){
15484         this.selections.remove(r);
15485     },
15486
15487     // private
15488     onRowUpdated : function(v, index, r){
15489         if(this.isSelected(r)){
15490             v.onRowSelect(index);
15491         }
15492     },
15493
15494     /**
15495      * Select records.
15496      * @param {Array} records The records to select
15497      * @param {Boolean} keepExisting (optional) True to keep existing selections
15498      */
15499     selectRecords : function(records, keepExisting){
15500         if(!keepExisting){
15501             this.clearSelections();
15502         }
15503         var ds = this.grid.dataSource;
15504         for(var i = 0, len = records.length; i < len; i++){
15505             this.selectRow(ds.indexOf(records[i]), true);
15506         }
15507     },
15508
15509     /**
15510      * Gets the number of selected rows.
15511      * @return {Number}
15512      */
15513     getCount : function(){
15514         return this.selections.length;
15515     },
15516
15517     /**
15518      * Selects the first row in the grid.
15519      */
15520     selectFirstRow : function(){
15521         this.selectRow(0);
15522     },
15523
15524     /**
15525      * Select the last row.
15526      * @param {Boolean} keepExisting (optional) True to keep existing selections
15527      */
15528     selectLastRow : function(keepExisting){
15529         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15530     },
15531
15532     /**
15533      * Selects the row immediately following the last selected row.
15534      * @param {Boolean} keepExisting (optional) True to keep existing selections
15535      */
15536     selectNext : function(keepExisting){
15537         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15538             this.selectRow(this.last+1, keepExisting);
15539             this.grid.getView().focusRow(this.last);
15540         }
15541     },
15542
15543     /**
15544      * Selects the row that precedes the last selected row.
15545      * @param {Boolean} keepExisting (optional) True to keep existing selections
15546      */
15547     selectPrevious : function(keepExisting){
15548         if(this.last){
15549             this.selectRow(this.last-1, keepExisting);
15550             this.grid.getView().focusRow(this.last);
15551         }
15552     },
15553
15554     /**
15555      * Returns the selected records
15556      * @return {Array} Array of selected records
15557      */
15558     getSelections : function(){
15559         return [].concat(this.selections.items);
15560     },
15561
15562     /**
15563      * Returns the first selected record.
15564      * @return {Record}
15565      */
15566     getSelected : function(){
15567         return this.selections.itemAt(0);
15568     },
15569
15570
15571     /**
15572      * Clears all selections.
15573      */
15574     clearSelections : function(fast){
15575         if(this.locked) return;
15576         if(fast !== true){
15577             var ds = this.grid.dataSource;
15578             var s = this.selections;
15579             s.each(function(r){
15580                 this.deselectRow(ds.indexOfId(r.id));
15581             }, this);
15582             s.clear();
15583         }else{
15584             this.selections.clear();
15585         }
15586         this.last = false;
15587     },
15588
15589
15590     /**
15591      * Selects all rows.
15592      */
15593     selectAll : function(){
15594         if(this.locked) return;
15595         this.selections.clear();
15596         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15597             this.selectRow(i, true);
15598         }
15599     },
15600
15601     /**
15602      * Returns True if there is a selection.
15603      * @return {Boolean}
15604      */
15605     hasSelection : function(){
15606         return this.selections.length > 0;
15607     },
15608
15609     /**
15610      * Returns True if the specified row is selected.
15611      * @param {Number/Record} record The record or index of the record to check
15612      * @return {Boolean}
15613      */
15614     isSelected : function(index){
15615         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15616         return (r && this.selections.key(r.id) ? true : false);
15617     },
15618
15619     /**
15620      * Returns True if the specified record id is selected.
15621      * @param {String} id The id of record to check
15622      * @return {Boolean}
15623      */
15624     isIdSelected : function(id){
15625         return (this.selections.key(id) ? true : false);
15626     },
15627
15628     // private
15629     handleMouseDown : function(e, t){
15630         var view = this.grid.getView(), rowIndex;
15631         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15632             return;
15633         };
15634         if(e.shiftKey && this.last !== false){
15635             var last = this.last;
15636             this.selectRange(last, rowIndex, e.ctrlKey);
15637             this.last = last; // reset the last
15638             view.focusRow(rowIndex);
15639         }else{
15640             var isSelected = this.isSelected(rowIndex);
15641             if(e.button !== 0 && isSelected){
15642                 view.focusRow(rowIndex);
15643             }else if(e.ctrlKey && isSelected){
15644                 this.deselectRow(rowIndex);
15645             }else if(!isSelected){
15646                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15647                 view.focusRow(rowIndex);
15648             }
15649         }
15650         this.fireEvent("afterselectionchange", this);
15651     },
15652     // private
15653     handleDragableRowClick :  function(grid, rowIndex, e) 
15654     {
15655         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15656             this.selectRow(rowIndex, false);
15657             grid.view.focusRow(rowIndex);
15658              this.fireEvent("afterselectionchange", this);
15659         }
15660     },
15661     
15662     /**
15663      * Selects multiple rows.
15664      * @param {Array} rows Array of the indexes of the row to select
15665      * @param {Boolean} keepExisting (optional) True to keep existing selections
15666      */
15667     selectRows : function(rows, keepExisting){
15668         if(!keepExisting){
15669             this.clearSelections();
15670         }
15671         for(var i = 0, len = rows.length; i < len; i++){
15672             this.selectRow(rows[i], true);
15673         }
15674     },
15675
15676     /**
15677      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15678      * @param {Number} startRow The index of the first row in the range
15679      * @param {Number} endRow The index of the last row in the range
15680      * @param {Boolean} keepExisting (optional) True to retain existing selections
15681      */
15682     selectRange : function(startRow, endRow, keepExisting){
15683         if(this.locked) return;
15684         if(!keepExisting){
15685             this.clearSelections();
15686         }
15687         if(startRow <= endRow){
15688             for(var i = startRow; i <= endRow; i++){
15689                 this.selectRow(i, true);
15690             }
15691         }else{
15692             for(var i = startRow; i >= endRow; i--){
15693                 this.selectRow(i, true);
15694             }
15695         }
15696     },
15697
15698     /**
15699      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15700      * @param {Number} startRow The index of the first row in the range
15701      * @param {Number} endRow The index of the last row in the range
15702      */
15703     deselectRange : function(startRow, endRow, preventViewNotify){
15704         if(this.locked) return;
15705         for(var i = startRow; i <= endRow; i++){
15706             this.deselectRow(i, preventViewNotify);
15707         }
15708     },
15709
15710     /**
15711      * Selects a row.
15712      * @param {Number} row The index of the row to select
15713      * @param {Boolean} keepExisting (optional) True to keep existing selections
15714      */
15715     selectRow : function(index, keepExisting, preventViewNotify){
15716         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15717         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15718             if(!keepExisting || this.singleSelect){
15719                 this.clearSelections();
15720             }
15721             var r = this.grid.dataSource.getAt(index);
15722             this.selections.add(r);
15723             this.last = this.lastActive = index;
15724             if(!preventViewNotify){
15725                 this.grid.getView().onRowSelect(index);
15726             }
15727             this.fireEvent("rowselect", this, index, r);
15728             this.fireEvent("selectionchange", this);
15729         }
15730     },
15731
15732     /**
15733      * Deselects a row.
15734      * @param {Number} row The index of the row to deselect
15735      */
15736     deselectRow : function(index, preventViewNotify){
15737         if(this.locked) return;
15738         if(this.last == index){
15739             this.last = false;
15740         }
15741         if(this.lastActive == index){
15742             this.lastActive = false;
15743         }
15744         var r = this.grid.dataSource.getAt(index);
15745         this.selections.remove(r);
15746         if(!preventViewNotify){
15747             this.grid.getView().onRowDeselect(index);
15748         }
15749         this.fireEvent("rowdeselect", this, index);
15750         this.fireEvent("selectionchange", this);
15751     },
15752
15753     // private
15754     restoreLast : function(){
15755         if(this._last){
15756             this.last = this._last;
15757         }
15758     },
15759
15760     // private
15761     acceptsNav : function(row, col, cm){
15762         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15763     },
15764
15765     // private
15766     onEditorKey : function(field, e){
15767         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15768         if(k == e.TAB){
15769             e.stopEvent();
15770             ed.completeEdit();
15771             if(e.shiftKey){
15772                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15773             }else{
15774                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15775             }
15776         }else if(k == e.ENTER && !e.ctrlKey){
15777             e.stopEvent();
15778             ed.completeEdit();
15779             if(e.shiftKey){
15780                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15781             }else{
15782                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15783             }
15784         }else if(k == e.ESC){
15785             ed.cancelEdit();
15786         }
15787         if(newCell){
15788             g.startEditing(newCell[0], newCell[1]);
15789         }
15790     }
15791 });/*
15792  * - LGPL
15793  *
15794  * element
15795  * 
15796  */
15797
15798 /**
15799  * @class Roo.bootstrap.MessageBar
15800  * @extends Roo.bootstrap.Component
15801  * Bootstrap MessageBar class
15802  * @cfg {String} html contents of the MessageBar
15803  * @cfg {String} weight (info | success | warning | danger) default info
15804  * @cfg {String} beforeClass insert the bar before the given class
15805  * @cfg {Boolean} closable (true | false) default false
15806  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15807  * 
15808  * @constructor
15809  * Create a new Element
15810  * @param {Object} config The config object
15811  */
15812
15813 Roo.bootstrap.MessageBar = function(config){
15814     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15815 };
15816
15817 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15818     
15819     html: '',
15820     weight: 'info',
15821     closable: false,
15822     fixed: false,
15823     beforeClass: 'bootstrap-sticky-wrap',
15824     
15825     getAutoCreate : function(){
15826         
15827         var cfg = {
15828             tag: 'div',
15829             cls: 'alert alert-dismissable alert-' + this.weight,
15830             cn: [
15831                 {
15832                     tag: 'span',
15833                     cls: 'message',
15834                     html: this.html || ''
15835                 }
15836             ]
15837         }
15838         
15839         if(this.fixed){
15840             cfg.cls += ' alert-messages-fixed';
15841         }
15842         
15843         if(this.closable){
15844             cfg.cn.push({
15845                 tag: 'button',
15846                 cls: 'close',
15847                 html: 'x'
15848             });
15849         }
15850         
15851         return cfg;
15852     },
15853     
15854     onRender : function(ct, position)
15855     {
15856         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15857         
15858         if(!this.el){
15859             var cfg = Roo.apply({},  this.getAutoCreate());
15860             cfg.id = Roo.id();
15861             
15862             if (this.cls) {
15863                 cfg.cls += ' ' + this.cls;
15864             }
15865             if (this.style) {
15866                 cfg.style = this.style;
15867             }
15868             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15869             
15870             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15871         }
15872         
15873         this.el.select('>button.close').on('click', this.hide, this);
15874         
15875     },
15876     
15877     show : function()
15878     {
15879         if (!this.rendered) {
15880             this.render();
15881         }
15882         
15883         this.el.show();
15884         
15885         this.fireEvent('show', this);
15886         
15887     },
15888     
15889     hide : function()
15890     {
15891         if (!this.rendered) {
15892             this.render();
15893         }
15894         
15895         this.el.hide();
15896         
15897         this.fireEvent('hide', this);
15898     },
15899     
15900     update : function()
15901     {
15902 //        var e = this.el.dom.firstChild;
15903 //        
15904 //        if(this.closable){
15905 //            e = e.nextSibling;
15906 //        }
15907 //        
15908 //        e.data = this.html || '';
15909
15910         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15911     }
15912    
15913 });
15914
15915  
15916
15917