871aaf4d61edd8335ddff04b6070e9eb396c3d7e
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position){
306         
307         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472     
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = this.html || cfg.html;
489         
490         if (this.toggle == true) {
491             cfg={
492                 tag: 'div',
493                 cls: 'slider-frame roo-button',
494                 cn: [
495                     {
496                         tag: 'span',
497                         'data-on-text':'ON',
498                         'data-off-text':'OFF',
499                         cls: 'slider-button',
500                         html: this.offtext
501                     }
502                 ]
503             };
504             
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 cfg.cls += ' '+this.weight;
507             }
508             
509             return cfg;
510         }
511         
512         if (this.isClose) {
513             cfg.cls += ' close';
514             
515             cfg["aria-hidden"] = true;
516             
517             cfg.html = "&times;";
518             
519             return cfg;
520         }
521         
522          
523         if (this.theme==='default') {
524             cfg.cls = 'btn roo-button';
525             
526             //if (this.parentType != 'Navbar') {
527             this.weight = this.weight.length ?  this.weight : 'default';
528             //}
529             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
530                 
531                 cfg.cls += ' btn-' + this.weight;
532             }
533         } else if (this.theme==='glow') {
534             
535             cfg.tag = 'a';
536             cfg.cls = 'btn-glow roo-button';
537             
538             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
539                 
540                 cfg.cls += ' ' + this.weight;
541             }
542         }
543    
544         
545         if (this.inverse) {
546             this.cls += ' inverse';
547         }
548         
549         
550         if (this.active) {
551             cfg.cls += ' active';
552         }
553         
554         if (this.disabled) {
555             cfg.disabled = 'disabled';
556         }
557         
558         if (this.items) {
559             Roo.log('changing to ul' );
560             cfg.tag = 'ul';
561             this.glyphicon = 'caret';
562         }
563         
564         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
565          
566         //gsRoo.log(this.parentType);
567         if (this.parentType === 'Navbar' && !this.parent().bar) {
568             Roo.log('changing to li?');
569             
570             cfg.tag = 'li';
571             
572             cfg.cls = '';
573             cfg.cn =  [{
574                 tag : 'a',
575                 cls : 'roo-button',
576                 html : this.html,
577                 href : this.href || '#'
578             }];
579             if (this.menu) {
580                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
581                 cfg.cls += ' dropdown';
582             }   
583             
584             delete cfg.html;
585             
586         }
587         
588        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
589         
590         if (this.glyphicon) {
591             cfg.html = ' ' + cfg.html;
592             
593             cfg.cn = [
594                 {
595                     tag: 'span',
596                     cls: 'glyphicon glyphicon-' + this.glyphicon
597                 }
598             ];
599         }
600         
601         if (this.badge) {
602             cfg.html += ' ';
603             
604             cfg.tag = 'a';
605             
606 //            cfg.cls='btn roo-button';
607             
608             cfg.href=this.href;
609             
610             var value = cfg.html;
611             
612             if(this.glyphicon){
613                 value = {
614                             tag: 'span',
615                             cls: 'glyphicon glyphicon-' + this.glyphicon,
616                             html: this.html
617                         };
618                 
619             }
620             
621             cfg.cn = [
622                 value,
623                 {
624                     tag: 'span',
625                     cls: 'badge',
626                     html: this.badge
627                 }
628             ];
629             
630             cfg.html='';
631         }
632         
633         if (this.menu) {
634             cfg.cls += ' dropdown';
635             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
636         }
637         
638         if (cfg.tag !== 'a' && this.href !== '') {
639             throw "Tag must be a to set href.";
640         } else if (this.href.length > 0) {
641             cfg.href = this.href;
642         }
643         
644         if(this.removeClass){
645             cfg.cls = '';
646         }
647         
648         if(this.target){
649             cfg.target = this.target;
650         }
651         
652         return cfg;
653     },
654     initEvents: function() {
655        // Roo.log('init events?');
656 //        Roo.log(this.el.dom);
657        if (this.el.hasClass('roo-button')) {
658             this.el.on('click', this.onClick, this);
659        } else {
660             this.el.select('.roo-button').on('click', this.onClick, this);
661        }
662        
663        
664         
665     },
666     onClick : function(e)
667     {
668         if (this.disabled) {
669             return;
670         }
671         
672         Roo.log('button on click ');
673         if(this.preventDefault){
674             e.preventDefault();
675         }
676         if (this.pressed === true || this.pressed === false) {
677             this.pressed = !this.pressed;
678             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679             this.fireEvent('toggle', this, e, this.pressed);
680         }
681         
682         
683         this.fireEvent('click', this, e);
684     },
685     
686     /**
687      * Enables this button
688      */
689     enable : function()
690     {
691         this.disabled = false;
692         this.el.removeClass('disabled');
693     },
694     
695     /**
696      * Disable this button
697      */
698     disable : function()
699     {
700         this.disabled = true;
701         this.el.addClass('disabled');
702     },
703      /**
704      * sets the active state on/off, 
705      * @param {Boolean} state (optional) Force a particular state
706      */
707     setActive : function(v) {
708         
709         this.el[v ? 'addClass' : 'removeClass']('active');
710     },
711      /**
712      * toggles the current active state 
713      */
714     toggleActive : function()
715     {
716        var active = this.el.hasClass('active');
717        this.setActive(!active);
718        
719         
720     }
721     
722     
723     
724 });
725
726  /*
727  * - LGPL
728  *
729  * column
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.Column
735  * @extends Roo.bootstrap.Component
736  * Bootstrap Column class
737  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739  * @cfg {Number} md colspan out of 12 for computer-sized screens
740  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741  * @cfg {String} html content of column.
742  * 
743  * @constructor
744  * Create a new Column
745  * @param {Object} config The config object
746  */
747
748 Roo.bootstrap.Column = function(config){
749     Roo.bootstrap.Column.superclass.constructor.call(this, config);
750 };
751
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
753     
754     xs: null,
755     sm: null,
756     md: null,
757     lg: null,
758     html: '',
759     offset: 0,
760     
761     getAutoCreate : function(){
762         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
763         
764         cfg = {
765             tag: 'div',
766             cls: 'column'
767         };
768         
769         var settings=this;
770         ['xs','sm','md','lg'].map(function(size){
771             if (settings[size]) {
772                 cfg.cls += ' col-' + size + '-' + settings[size];
773             }
774         });
775         if (this.html.length) {
776             cfg.html = this.html;
777         }
778         
779         return cfg;
780     }
781    
782 });
783
784  
785
786  /*
787  * - LGPL
788  *
789  * page container.
790  * 
791  */
792
793
794 /**
795  * @class Roo.bootstrap.Container
796  * @extends Roo.bootstrap.Component
797  * Bootstrap Container class
798  * @cfg {Boolean} jumbotron is it a jumbotron element
799  * @cfg {String} html content of element
800  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802  * @cfg {String} header content of header (for panel)
803  * @cfg {String} footer content of footer (for panel)
804  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
805  *     
806  * @constructor
807  * Create a new Container
808  * @param {Object} config The config object
809  */
810
811 Roo.bootstrap.Container = function(config){
812     Roo.bootstrap.Container.superclass.constructor.call(this, config);
813 };
814
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
816     
817     jumbotron : false,
818     well: '',
819     panel : '',
820     header: '',
821     footer : '',
822     sticky: '',
823   
824      
825     getChildContainer : function() {
826         
827         if(!this.el){
828             return false;
829         }
830         
831         if (this.panel.length) {
832             return this.el.select('.panel-body',true).first();
833         }
834         
835         return this.el;
836     },
837     
838     
839     getAutoCreate : function(){
840         
841         var cfg = {
842             html : '',
843             cls : ''
844         };
845         if (this.jumbotron) {
846             cfg.cls = 'jumbotron';
847         }
848         if (this.cls) {
849             cfg.cls = this.cls + '';
850         }
851         
852         if (this.sticky.length) {
853             
854             var bd = Roo.get(document.body);
855             if (!bd.hasClass('bootstrap-sticky')) {
856                 bd.addClass('bootstrap-sticky');
857                 Roo.select('html',true).setStyle('height', '100%');
858             }
859              
860             cfg.cls += 'bootstrap-sticky-' + this.sticky;
861         }
862         
863         
864         if (this.well.length) {
865             switch (this.well) {
866                 case 'lg':
867                 case 'sm':
868                     cfg.cls +=' well well-' +this.well;
869                     break;
870                 default:
871                     cfg.cls +=' well';
872                     break;
873             }
874         }
875         
876         var body = cfg;
877         
878         if (this.panel.length) {
879             cfg.cls += ' panel panel-' + this.panel;
880             cfg.cn = [];
881             if (this.header.length) {
882                 cfg.cn.push({
883                     
884                     cls : 'panel-heading',
885                     cn : [{
886                         tag: 'h3',
887                         cls : 'panel-title',
888                         html : this.header
889                     }]
890                     
891                 });
892             }
893             body = false;
894             cfg.cn.push({
895                 cls : 'panel-body',
896                 html : this.html
897             });
898             
899             
900             if (this.footer.length) {
901                 cfg.cn.push({
902                     cls : 'panel-footer',
903                     html : this.footer
904                     
905                 });
906             }
907             
908         }
909         if (body) {
910             body.html = this.html || cfg.html;
911         }
912         if (!cfg.cls.length) {
913             cfg.cls =  'container';
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  /*
922  * - LGPL
923  *
924  * image
925  * 
926  */
927
928
929 /**
930  * @class Roo.bootstrap.Img
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Img class
933  * @cfg {Boolean} imgResponsive false | true
934  * @cfg {String} border rounded | circle | thumbnail
935  * @cfg {String} src image source
936  * @cfg {String} alt image alternative text
937  * @cfg {String} href a tag href
938  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
939  * 
940  * @constructor
941  * Create a new Input
942  * @param {Object} config The config object
943  */
944
945 Roo.bootstrap.Img = function(config){
946     Roo.bootstrap.Img.superclass.constructor.call(this, config);
947     
948     this.addEvents({
949         // img events
950         /**
951          * @event click
952          * The img click event for the img.
953          * @param {Roo.EventObject} e
954          */
955         "click" : true
956     });
957 };
958
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
960     
961     imgResponsive: true,
962     border: '',
963     src: '',
964     href: false,
965     target: false,
966
967     getAutoCreate : function(){
968         
969         var cfg = {
970             tag: 'img',
971             cls: 'img-responsive',
972             html : null
973         }
974         
975         cfg.html = this.html || cfg.html;
976         
977         cfg.src = this.src || cfg.src;
978         
979         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980             cfg.cls += ' img-' + this.border;
981         }
982         
983         if(this.alt){
984             cfg.alt = this.alt;
985         }
986         
987         if(this.href){
988             var a = {
989                 tag: 'a',
990                 href: this.href,
991                 cn: [
992                     cfg
993                 ]
994             }
995             
996             if(this.target){
997                 a.target = this.target;
998             }
999             
1000         }
1001         
1002         
1003         return (this.href) ? a : cfg;
1004     },
1005     
1006     initEvents: function() {
1007         
1008         if(!this.href){
1009             this.el.on('click', this.onClick, this);
1010         }
1011     },
1012     
1013     onClick : function(e)
1014     {
1015         Roo.log('img onclick');
1016         this.fireEvent('click', this, e);
1017     }
1018    
1019 });
1020
1021  /*
1022  * - LGPL
1023  *
1024  * header
1025  * 
1026  */
1027
1028 /**
1029  * @class Roo.bootstrap.Header
1030  * @extends Roo.bootstrap.Component
1031  * Bootstrap Header class
1032  * @cfg {String} html content of header
1033  * @cfg {Number} level (1|2|3|4|5|6) default 1
1034  * 
1035  * @constructor
1036  * Create a new Header
1037  * @param {Object} config The config object
1038  */
1039
1040
1041 Roo.bootstrap.Header  = function(config){
1042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1046     
1047     //href : false,
1048     html : false,
1049     level : 1,
1050     
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag: 'h' + (1 *this.level),
1057             html: this.html || 'fill in html'
1058         } ;
1059         
1060         return cfg;
1061     }
1062    
1063 });
1064
1065  
1066
1067  /*
1068  * Based on:
1069  * Ext JS Library 1.1.1
1070  * Copyright(c) 2006-2007, Ext JS, LLC.
1071  *
1072  * Originally Released Under LGPL - original licence link has changed is not relivant.
1073  *
1074  * Fork - LGPL
1075  * <script type="text/javascript">
1076  */
1077  
1078 /**
1079  * @class Roo.bootstrap.MenuMgr
1080  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1081  * @singleton
1082  */
1083 Roo.bootstrap.MenuMgr = function(){
1084    var menus, active, groups = {}, attached = false, lastShow = new Date();
1085
1086    // private - called when first menu is created
1087    function init(){
1088        menus = {};
1089        active = new Roo.util.MixedCollection();
1090        Roo.get(document).addKeyListener(27, function(){
1091            if(active.length > 0){
1092                hideAll();
1093            }
1094        });
1095    }
1096
1097    // private
1098    function hideAll(){
1099        if(active && active.length > 0){
1100            var c = active.clone();
1101            c.each(function(m){
1102                m.hide();
1103            });
1104        }
1105    }
1106
1107    // private
1108    function onHide(m){
1109        active.remove(m);
1110        if(active.length < 1){
1111            Roo.get(document).un("mouseup", onMouseDown);
1112             
1113            attached = false;
1114        }
1115    }
1116
1117    // private
1118    function onShow(m){
1119        var last = active.last();
1120        lastShow = new Date();
1121        active.add(m);
1122        if(!attached){
1123           Roo.get(document).on("mouseup", onMouseDown);
1124            
1125            attached = true;
1126        }
1127        if(m.parentMenu){
1128           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129           m.parentMenu.activeChild = m;
1130        }else if(last && last.isVisible()){
1131           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1132        }
1133    }
1134
1135    // private
1136    function onBeforeHide(m){
1137        if(m.activeChild){
1138            m.activeChild.hide();
1139        }
1140        if(m.autoHideTimer){
1141            clearTimeout(m.autoHideTimer);
1142            delete m.autoHideTimer;
1143        }
1144    }
1145
1146    // private
1147    function onBeforeShow(m){
1148        var pm = m.parentMenu;
1149        if(!pm && !m.allowOtherMenus){
1150            hideAll();
1151        }else if(pm && pm.activeChild && active != m){
1152            pm.activeChild.hide();
1153        }
1154    }
1155
1156    // private
1157    function onMouseDown(e){
1158         Roo.log("on MouseDown");
1159         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1160            hideAll();
1161         }
1162         
1163         
1164    }
1165
1166    // private
1167    function onBeforeCheck(mi, state){
1168        if(state){
1169            var g = groups[mi.group];
1170            for(var i = 0, l = g.length; i < l; i++){
1171                if(g[i] != mi){
1172                    g[i].setChecked(false);
1173                }
1174            }
1175        }
1176    }
1177
1178    return {
1179
1180        /**
1181         * Hides all menus that are currently visible
1182         */
1183        hideAll : function(){
1184             hideAll();  
1185        },
1186
1187        // private
1188        register : function(menu){
1189            if(!menus){
1190                init();
1191            }
1192            menus[menu.id] = menu;
1193            menu.on("beforehide", onBeforeHide);
1194            menu.on("hide", onHide);
1195            menu.on("beforeshow", onBeforeShow);
1196            menu.on("show", onShow);
1197            var g = menu.group;
1198            if(g && menu.events["checkchange"]){
1199                if(!groups[g]){
1200                    groups[g] = [];
1201                }
1202                groups[g].push(menu);
1203                menu.on("checkchange", onCheck);
1204            }
1205        },
1206
1207         /**
1208          * Returns a {@link Roo.menu.Menu} object
1209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210          * be used to generate and return a new Menu instance.
1211          */
1212        get : function(menu){
1213            if(typeof menu == "string"){ // menu id
1214                return menus[menu];
1215            }else if(menu.events){  // menu instance
1216                return menu;
1217            }
1218            /*else if(typeof menu.length == 'number'){ // array of menu items?
1219                return new Roo.bootstrap.Menu({items:menu});
1220            }else{ // otherwise, must be a config
1221                return new Roo.bootstrap.Menu(menu);
1222            }
1223            */
1224            return false;
1225        },
1226
1227        // private
1228        unregister : function(menu){
1229            delete menus[menu.id];
1230            menu.un("beforehide", onBeforeHide);
1231            menu.un("hide", onHide);
1232            menu.un("beforeshow", onBeforeShow);
1233            menu.un("show", onShow);
1234            var g = menu.group;
1235            if(g && menu.events["checkchange"]){
1236                groups[g].remove(menu);
1237                menu.un("checkchange", onCheck);
1238            }
1239        },
1240
1241        // private
1242        registerCheckable : function(menuItem){
1243            var g = menuItem.group;
1244            if(g){
1245                if(!groups[g]){
1246                    groups[g] = [];
1247                }
1248                groups[g].push(menuItem);
1249                menuItem.on("beforecheckchange", onBeforeCheck);
1250            }
1251        },
1252
1253        // private
1254        unregisterCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                groups[g].remove(menuItem);
1258                menuItem.un("beforecheckchange", onBeforeCheck);
1259            }
1260        }
1261    };
1262 }();/*
1263  * - LGPL
1264  *
1265  * menu
1266  * 
1267  */
1268
1269 /**
1270  * @class Roo.bootstrap.Menu
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Menu class - container for MenuItems
1273  * @cfg {String} type type of menu
1274  * 
1275  * @constructor
1276  * Create a new Menu
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Menu = function(config){
1282     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283     if (this.registerMenu) {
1284         Roo.bootstrap.MenuMgr.register(this);
1285     }
1286     this.addEvents({
1287         /**
1288          * @event beforeshow
1289          * Fires before this menu is displayed
1290          * @param {Roo.menu.Menu} this
1291          */
1292         beforeshow : true,
1293         /**
1294          * @event beforehide
1295          * Fires before this menu is hidden
1296          * @param {Roo.menu.Menu} this
1297          */
1298         beforehide : true,
1299         /**
1300          * @event show
1301          * Fires after this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         show : true,
1305         /**
1306          * @event hide
1307          * Fires after this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         hide : true,
1311         /**
1312          * @event click
1313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314          * @param {Roo.menu.Menu} this
1315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316          * @param {Roo.EventObject} e
1317          */
1318         click : true,
1319         /**
1320          * @event mouseover
1321          * Fires when the mouse is hovering over this menu
1322          * @param {Roo.menu.Menu} this
1323          * @param {Roo.EventObject} e
1324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1325          */
1326         mouseover : true,
1327         /**
1328          * @event mouseout
1329          * Fires when the mouse exits this menu
1330          * @param {Roo.menu.Menu} this
1331          * @param {Roo.EventObject} e
1332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1333          */
1334         mouseout : true,
1335         /**
1336          * @event itemclick
1337          * Fires when a menu item contained in this menu is clicked
1338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339          * @param {Roo.EventObject} e
1340          */
1341         itemclick: true
1342     });
1343     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1347     
1348    /// html : false,
1349     //align : '',
1350     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1351     type: false,
1352     /**
1353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1354      */
1355     registerMenu : true,
1356     
1357     menuItems :false, // stores the menu items..
1358     
1359     hidden:true,
1360     
1361     parentMenu : false,
1362     
1363     getChildContainer : function() {
1364         return this.el;  
1365     },
1366     
1367     getAutoCreate : function(){
1368          
1369         //if (['right'].indexOf(this.align)!==-1) {
1370         //    cfg.cn[1].cls += ' pull-right'
1371         //}
1372         var cfg = {
1373             tag : 'ul',
1374             cls : 'dropdown-menu' ,
1375             style : 'z-index:1000'
1376             
1377         }
1378         
1379         if (this.type === 'submenu') {
1380             cfg.cls = 'submenu active'
1381         }
1382         
1383         return cfg;
1384     },
1385     initEvents : function() {
1386         
1387        // Roo.log("ADD event");
1388        // Roo.log(this.triggerEl.dom);
1389         this.triggerEl.on('click', this.onTriggerPress, this);
1390         this.triggerEl.addClass('dropdown-toggle');
1391         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1392
1393         this.el.on("mouseover", this.onMouseOver, this);
1394         this.el.on("mouseout", this.onMouseOut, this);
1395         
1396         
1397     },
1398     findTargetItem : function(e){
1399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1400         if(!t){
1401             return false;
1402         }
1403         //Roo.log(t);         Roo.log(t.id);
1404         if(t && t.id){
1405             //Roo.log(this.menuitems);
1406             return this.menuitems.get(t.id);
1407             
1408             //return this.items.get(t.menuItemId);
1409         }
1410         
1411         return false;
1412     },
1413     onClick : function(e){
1414         Roo.log("menu.onClick");
1415         var t = this.findTargetItem(e);
1416         if(!t){
1417             return;
1418         }
1419         Roo.log(e);
1420         /*
1421         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1422             if(t == this.activeItem && t.shouldDeactivate(e)){
1423                 this.activeItem.deactivate();
1424                 delete this.activeItem;
1425                 return;
1426             }
1427             if(t.canActivate){
1428                 this.setActiveItem(t, true);
1429             }
1430             return;
1431             
1432             
1433         }
1434         */
1435         Roo.log('pass click event');
1436         
1437         t.onClick(e);
1438         
1439         this.fireEvent("click", this, t, e);
1440         
1441         this.hide();
1442     },
1443      onMouseOver : function(e){
1444         var t  = this.findTargetItem(e);
1445         //Roo.log(t);
1446         //if(t){
1447         //    if(t.canActivate && !t.disabled){
1448         //        this.setActiveItem(t, true);
1449         //    }
1450         //}
1451         
1452         this.fireEvent("mouseover", this, e, t);
1453     },
1454     isVisible : function(){
1455         return !this.hidden;
1456     },
1457      onMouseOut : function(e){
1458         var t  = this.findTargetItem(e);
1459         
1460         //if(t ){
1461         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1462         //        this.activeItem.deactivate();
1463         //        delete this.activeItem;
1464         //    }
1465         //}
1466         this.fireEvent("mouseout", this, e, t);
1467     },
1468     
1469     
1470     /**
1471      * Displays this menu relative to another element
1472      * @param {String/HTMLElement/Roo.Element} element The element to align to
1473      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474      * the element (defaults to this.defaultAlign)
1475      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1476      */
1477     show : function(el, pos, parentMenu){
1478         this.parentMenu = parentMenu;
1479         if(!this.el){
1480             this.render();
1481         }
1482         this.fireEvent("beforeshow", this);
1483         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1484     },
1485      /**
1486      * Displays this menu at a specific xy position
1487      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489      */
1490     showAt : function(xy, parentMenu, /* private: */_e){
1491         this.parentMenu = parentMenu;
1492         if(!this.el){
1493             this.render();
1494         }
1495         if(_e !== false){
1496             this.fireEvent("beforeshow", this);
1497             
1498             //xy = this.el.adjustForConstraints(xy);
1499         }
1500         //this.el.setXY(xy);
1501         //this.el.show();
1502         this.hideMenuItems();
1503         this.hidden = false;
1504         this.triggerEl.addClass('open');
1505         this.focus();
1506         this.fireEvent("show", this);
1507     },
1508     
1509     focus : function(){
1510         return;
1511         if(!this.hidden){
1512             this.doFocus.defer(50, this);
1513         }
1514     },
1515
1516     doFocus : function(){
1517         if(!this.hidden){
1518             this.focusEl.focus();
1519         }
1520     },
1521
1522     /**
1523      * Hides this menu and optionally all parent menus
1524      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1525      */
1526     hide : function(deep){
1527         
1528         this.hideMenuItems();
1529         if(this.el && this.isVisible()){
1530             this.fireEvent("beforehide", this);
1531             if(this.activeItem){
1532                 this.activeItem.deactivate();
1533                 this.activeItem = null;
1534             }
1535             this.triggerEl.removeClass('open');;
1536             this.hidden = true;
1537             this.fireEvent("hide", this);
1538         }
1539         if(deep === true && this.parentMenu){
1540             this.parentMenu.hide(true);
1541         }
1542     },
1543     
1544     onTriggerPress  : function(e)
1545     {
1546         
1547         Roo.log('trigger press');
1548         //Roo.log(e.getTarget());
1549        // Roo.log(this.triggerEl.dom);
1550         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1551             return;
1552         }
1553         if (this.isVisible()) {
1554             Roo.log('hide');
1555             this.hide();
1556         } else {
1557             this.show(this.triggerEl, false, false);
1558         }
1559         
1560         
1561     },
1562     
1563          
1564        
1565     
1566     hideMenuItems : function()
1567     {
1568         //$(backdrop).remove()
1569         Roo.select('.open',true).each(function(aa) {
1570             
1571             aa.removeClass('open');
1572           //var parent = getParent($(this))
1573           //var relatedTarget = { relatedTarget: this }
1574           
1575            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576           //if (e.isDefaultPrevented()) return
1577            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1578         })
1579     },
1580     addxtypeChild : function (tree, cntr) {
1581         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1582           
1583         this.menuitems.add(comp);
1584         return comp;
1585
1586     },
1587     getEl : function()
1588     {
1589         Roo.log(this.el);
1590         return this.el;
1591     }
1592 });
1593
1594  
1595  /*
1596  * - LGPL
1597  *
1598  * menu item
1599  * 
1600  */
1601
1602
1603 /**
1604  * @class Roo.bootstrap.MenuItem
1605  * @extends Roo.bootstrap.Component
1606  * Bootstrap MenuItem class
1607  * @cfg {String} html the menu label
1608  * @cfg {String} href the link
1609  * @cfg {Boolean} preventDefault (true | false) default true
1610  * 
1611  * 
1612  * @constructor
1613  * Create a new MenuItem
1614  * @param {Object} config The config object
1615  */
1616
1617
1618 Roo.bootstrap.MenuItem = function(config){
1619     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1620     this.addEvents({
1621         // raw events
1622         /**
1623          * @event click
1624          * The raw click event for the entire grid.
1625          * @param {Roo.EventObject} e
1626          */
1627         "click" : true
1628     });
1629 };
1630
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1632     
1633     href : false,
1634     html : false,
1635     preventDefault: true,
1636     
1637     getAutoCreate : function(){
1638         var cfg= {
1639             tag: 'li',
1640         cls: 'dropdown-menu-item',
1641             cn: [
1642             {
1643                 tag : 'a',
1644                 href : '#',
1645                 html : 'Link'
1646             }
1647             ]
1648     };
1649         
1650         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1652         return cfg;
1653     },
1654     
1655     initEvents: function() {
1656         
1657         //this.el.select('a').on('click', this.onClick, this);
1658         
1659     },
1660     onClick : function(e)
1661     {
1662         Roo.log('item on click ');
1663         //if(this.preventDefault){
1664         //    e.preventDefault();
1665         //}
1666         //this.parent().hideMenuItems();
1667         
1668         this.fireEvent('click', this, e);
1669     },
1670     getEl : function()
1671     {
1672         return this.el;
1673     }
1674 });
1675
1676  
1677
1678  /*
1679  * - LGPL
1680  *
1681  * menu separator
1682  * 
1683  */
1684
1685
1686 /**
1687  * @class Roo.bootstrap.MenuSeparator
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap MenuSeparator class
1690  * 
1691  * @constructor
1692  * Create a new MenuItem
1693  * @param {Object} config The config object
1694  */
1695
1696
1697 Roo.bootstrap.MenuSeparator = function(config){
1698     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1699 };
1700
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1702     
1703     getAutoCreate : function(){
1704         var cfg = {
1705             cls: 'divider',
1706             tag : 'li'
1707         };
1708         
1709         return cfg;
1710     }
1711    
1712 });
1713
1714  
1715
1716  
1717 /*
1718 <div class="modal fade">
1719   <div class="modal-dialog">
1720     <div class="modal-content">
1721       <div class="modal-header">
1722         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1723         <h4 class="modal-title">Modal title</h4>
1724       </div>
1725       <div class="modal-body">
1726         <p>One fine body&hellip;</p>
1727       </div>
1728       <div class="modal-footer">
1729         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730         <button type="button" class="btn btn-primary">Save changes</button>
1731       </div>
1732     </div><!-- /.modal-content -->
1733   </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1735 */
1736 /*
1737  * - LGPL
1738  *
1739  * page contgainer.
1740  * 
1741  */
1742
1743 /**
1744  * @class Roo.bootstrap.Modal
1745  * @extends Roo.bootstrap.Component
1746  * Bootstrap Modal class
1747  * @cfg {String} title Title of dialog
1748  * @cfg {Array} buttons Array of buttons or standard button set..
1749  * 
1750  * @constructor
1751  * Create a new Modal Dialog
1752  * @param {Object} config The config object
1753  */
1754
1755 Roo.bootstrap.Modal = function(config){
1756     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1757     this.addEvents({
1758         // raw events
1759         /**
1760          * @event btnclick
1761          * The raw btnclick event for the button
1762          * @param {Roo.EventObject} e
1763          */
1764         "btnclick" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1769     
1770     title : 'test dialog',
1771    
1772     buttons : false,
1773     
1774     onRender : function(ct, position)
1775     {
1776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1777      
1778         if(!this.el){
1779             var cfg = Roo.apply({},  this.getAutoCreate());
1780             cfg.id = Roo.id();
1781             //if(!cfg.name){
1782             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1783             //}
1784             //if (!cfg.name.length) {
1785             //    delete cfg.name;
1786            // }
1787             if (this.cls) {
1788                 cfg.cls += ' ' + this.cls;
1789             }
1790             if (this.style) {
1791                 cfg.style = this.style;
1792             }
1793             this.el = Roo.get(document.body).createChild(cfg, position);
1794         }
1795         //var type = this.el.dom.type;
1796         
1797         if(this.tabIndex !== undefined){
1798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1799         }
1800         
1801         
1802         
1803         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804         this.maskEl.enableDisplayMode("block");
1805         this.maskEl.hide();
1806         //this.el.addClass("x-dlg-modal");
1807     
1808         if (this.buttons) {
1809             Roo.each(this.buttons, function(bb) {
1810                 b = Roo.apply({}, bb);
1811                 b.xns = b.xns || Roo.bootstrap;
1812                 b.xtype = b.xtype || 'Button';
1813                 if (typeof(b.listeners) == 'undefined') {
1814                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1815                 }
1816                 
1817                 var btn = Roo.factory(b);
1818                 
1819                 btn.onRender(this.el.select('.modal-footer').first());
1820                 
1821             },this);
1822         }
1823         // render the children.
1824         var nitems = [];
1825         
1826         if(typeof(this.items) != 'undefined'){
1827             var items = this.items;
1828             delete this.items;
1829
1830             for(var i =0;i < items.length;i++) {
1831                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1832             }
1833         }
1834         
1835         this.items = nitems;
1836         this.initEvents();
1837         //this.el.addClass([this.fieldClass, this.cls]);
1838         
1839     },
1840     getAutoCreate : function(){
1841         
1842         
1843         var bdy = {
1844                 cls : 'modal-body',
1845                 html : this.html || ''
1846         };
1847         
1848          
1849         return modal = {
1850             cls: "modal fade",
1851             cn : [
1852                 {
1853                     cls: "modal-dialog",
1854                     cn : [
1855                         {
1856                             cls : "modal-content",
1857                             cn : [
1858                                 {
1859                                     cls : 'modal-header',
1860                                     cn : [
1861                                         {
1862                                             tag: 'button',
1863                                             cls : 'close',
1864                                             html : '&times'
1865                                         },
1866                                         {
1867                                             tag: 'h4',
1868                                             cls : 'modal-title',
1869                                             html : this.title
1870                                         }
1871                                     
1872                                     ]
1873                                 },
1874                                 bdy,
1875                                 {
1876                                     cls : 'modal-footer' 
1877                                 }
1878                                 
1879                                 
1880                             ]
1881                             
1882                         }
1883                     ]
1884                         
1885                 }
1886             ]
1887             
1888             
1889         };
1890           
1891     },
1892     getChildContainer : function() {
1893          
1894          return this.el.select('.modal-body',true).first();
1895         
1896     },
1897     getButtonContainer : function() {
1898          return this.el.select('.modal-footer',true).first();
1899         
1900     },
1901     initEvents : function()
1902     {
1903         this.el.select('.modal-header .close').on('click', this.hide, this);
1904 //        
1905 //        this.addxtype(this);
1906     },
1907     show : function() {
1908         
1909         if (!this.rendered) {
1910             this.render();
1911         }
1912        
1913         this.el.addClass('on');
1914         this.el.removeClass('fade');
1915         this.el.setStyle('display', 'block');
1916         Roo.get(document.body).addClass("x-body-masked");
1917         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1918         this.maskEl.show();
1919         this.el.setStyle('zIndex', '10001');
1920         this.fireEvent('show', this);
1921         
1922         
1923     },
1924     hide : function()
1925     {
1926         Roo.log('Modal hide?!');
1927         this.maskEl.hide();
1928         Roo.get(document.body).removeClass("x-body-masked");
1929         this.el.removeClass('on');
1930         this.el.addClass('fade');
1931         this.el.setStyle('display', 'none');
1932         this.fireEvent('hide', this);
1933     },
1934     onButtonClick: function(btn,e)
1935     {
1936         //Roo.log([a,b,c]);
1937         this.fireEvent('btnclick', btn.name, e);
1938     }
1939 });
1940
1941
1942 Roo.apply(Roo.bootstrap.Modal,  {
1943     /**
1944          * Button config that displays a single OK button
1945          * @type Object
1946          */
1947         OK :  [{
1948             name : 'ok',
1949             weight : 'primary',
1950             html : 'OK'
1951         }], 
1952         /**
1953          * Button config that displays Yes and No buttons
1954          * @type Object
1955          */
1956         YESNO : [
1957             {
1958                 name  : 'no',
1959                 html : 'No'
1960             },
1961             {
1962                 name  :'yes',
1963                 weight : 'primary',
1964                 html : 'Yes'
1965             }
1966         ],
1967         
1968         /**
1969          * Button config that displays OK and Cancel buttons
1970          * @type Object
1971          */
1972         OKCANCEL : [
1973             {
1974                name : 'cancel',
1975                 html : 'Cancel'
1976             },
1977             {
1978                 name : 'ok',
1979                 weight : 'primary',
1980                 html : 'OK'
1981             }
1982         ],
1983         /**
1984          * Button config that displays Yes, No and Cancel buttons
1985          * @type Object
1986          */
1987         YESNOCANCEL : [
1988             {
1989                 name : 'yes',
1990                 weight : 'primary',
1991                 html : 'Yes'
1992             },
1993             {
1994                 name : 'no',
1995                 html : 'No'
1996             },
1997             {
1998                 name : 'cancel',
1999                 html : 'Cancel'
2000             }
2001         ]
2002 });
2003  /*
2004  * - LGPL
2005  *
2006  * navbar
2007  * 
2008  */
2009
2010 /**
2011  * @class Roo.bootstrap.Navbar
2012  * @extends Roo.bootstrap.Component
2013  * Bootstrap Navbar class
2014  * @cfg {Boolean} sidebar has side bar
2015  * @cfg {Boolean} bar is a bar?
2016  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017  * @cfg {String} brand what is brand
2018  * @cfg {Boolean} inverse is inverted color
2019  * @cfg {String} type (nav | pills | tabs)
2020  * @cfg {Boolean} arrangement stacked | justified
2021  * @cfg {String} align (left | right) alignment
2022  * @cfg {String} brand_href href of the brand
2023  * @cfg {Boolean} main (true|false) main nav bar? default false
2024  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2025  *
2026  * 
2027  * @constructor
2028  * Create a new Navbar
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.Navbar = function(config){
2034     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2038     
2039     sidebar: false,
2040     
2041     bar: false,
2042     brand: '',
2043     inverse: false,
2044     position: '',
2045     align : false,
2046     type: 'nav',
2047     arrangement: '',
2048     brand_href: false,
2049     main : false,
2050     loadMask : false,
2051     
2052     getAutoCreate : function(){
2053         var cfg = {
2054             cls : 'navbar'
2055         };
2056         
2057         if (this.sidebar === true) {
2058             cfg = {
2059                 tag: 'div',
2060                 cls: 'sidebar-nav'
2061             };
2062             return cfg;
2063         }
2064         
2065         if (this.bar === true) {
2066             cfg = {
2067                 tag: 'nav',
2068                 cls: 'navbar',
2069                 role: 'navigation',
2070                 cn: [
2071                     {
2072                         tag: 'div',
2073                         cls: 'navbar-header',
2074                         cn: [
2075                             {
2076                             tag: 'button',
2077                             type: 'button',
2078                             cls: 'navbar-toggle',
2079                             'data-toggle': 'collapse',
2080                             cn: [
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'sr-only',
2084                                     html: 'Toggle navigation'
2085                                 },
2086                                 {
2087                                     tag: 'span',
2088                                     cls: 'icon-bar'
2089                                 },
2090                                 {
2091                                     tag: 'span',
2092                                     cls: 'icon-bar'
2093                                 },
2094                                 {
2095                                     tag: 'span',
2096                                     cls: 'icon-bar'
2097                                 }
2098                             ]
2099                             }
2100                         ]
2101                     },
2102                     {
2103                     tag: 'div',
2104                     cls: 'collapse navbar-collapse'
2105                     }
2106                 ]
2107             };
2108             
2109             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2110             
2111             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112                 cfg.cls += ' navbar-' + this.position;
2113                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2114             }
2115             
2116             if (this.brand !== '') {
2117                 cfg.cn[0].cn.push({
2118                     tag: 'a',
2119                     href: this.brand_href ? this.brand_href : '#',
2120                     cls: 'navbar-brand',
2121                     cn: [
2122                     this.brand
2123                     ]
2124                 });
2125             }
2126             
2127             if(this.main){
2128                 cfg.cls += ' main-nav';
2129             }
2130             
2131             
2132             return cfg;
2133         
2134         } else if (this.bar === false) {
2135             
2136         } else {
2137             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2138         }
2139         
2140         cfg.cn = [
2141             {
2142                 cls: 'nav',
2143                 tag : 'ul'
2144             }
2145         ];
2146         
2147         if (['tabs','pills'].indexOf(this.type)!==-1) {
2148             cfg.cn[0].cls += ' nav-' + this.type
2149         } else {
2150             if (this.type!=='nav') {
2151             Roo.log('nav type must be nav/tabs/pills')
2152             }
2153             cfg.cn[0].cls += ' navbar-nav'
2154         }
2155         
2156         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157             cfg.cn[0].cls += ' nav-' + this.arrangement;
2158         }
2159         
2160         if (this.align === 'right') {
2161             cfg.cn[0].cls += ' navbar-right';
2162         }
2163         if (this.inverse) {
2164             cfg.cls += ' navbar-inverse';
2165             
2166         }
2167         
2168         
2169         return cfg;
2170     },
2171     
2172     initEvents :function ()
2173     {
2174         //Roo.log(this.el.select('.navbar-toggle',true));
2175         this.el.select('.navbar-toggle',true).on('click', function() {
2176            // Roo.log('click');
2177             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2178         }, this);
2179         
2180         var mark = {
2181             tag: "div",
2182             cls:"x-dlg-mask"
2183         }
2184         
2185         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2186         
2187         var size = this.el.getSize();
2188         this.maskEl.setSize(size.width, size.height);
2189         this.maskEl.enableDisplayMode("block");
2190         this.maskEl.hide();
2191         
2192         if(this.loadMask){
2193             this.maskEl.show();
2194         }
2195     },
2196     
2197     
2198     getChildContainer : function()
2199     {
2200         if (this.bar === true) {
2201             return this.el.select('.collapse',true).first();
2202         }
2203         
2204         return this.el;
2205     },
2206     
2207     mask : function()
2208     {
2209         this.maskEl.show();
2210     },
2211     
2212     unmask : function()
2213     {
2214         this.maskEl.hide();
2215     }
2216    
2217 });
2218
2219  
2220
2221  /*
2222  * - LGPL
2223  *
2224  * nav group
2225  * 
2226  */
2227
2228 /**
2229  * @class Roo.bootstrap.NavGroup
2230  * @extends Roo.bootstrap.Component
2231  * Bootstrap NavGroup class
2232  * @cfg {String} align left | right
2233  * @cfg {Boolean} inverse false | true
2234  * @cfg {String} type (nav|pills|tab) default nav
2235  * 
2236  * @constructor
2237  * Create a new nav group
2238  * @param {Object} config The config object
2239  */
2240
2241 Roo.bootstrap.NavGroup = function(config){
2242     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2243 };
2244
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2246     
2247     align: '',
2248     inverse: false,
2249     form: false,
2250     type: 'nav',
2251     
2252     getAutoCreate : function(){
2253         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2254         
2255         cfg = {
2256             tag : 'ul',
2257             cls: 'nav' 
2258         }
2259         
2260         if (['tabs','pills'].indexOf(this.type)!==-1) {
2261             cfg.cls += ' nav-' + this.type
2262         } else {
2263             if (this.type!=='nav') {
2264                 Roo.log('nav type must be nav/tabs/pills')
2265             }
2266             cfg.cls += ' navbar-nav'
2267         }
2268         
2269         if (this.parent().sidebar === true) {
2270             cfg = {
2271                 tag: 'ul',
2272                 cls: 'dashboard-menu'
2273             }
2274             
2275             return cfg;
2276         }
2277         
2278         if (this.form === true) {
2279             cfg = {
2280                 tag: 'form',
2281                 cls: 'navbar-form'
2282             }
2283             
2284             if (this.align === 'right') {
2285                 cfg.cls += ' navbar-right';
2286             } else {
2287                 cfg.cls += ' navbar-left';
2288             }
2289         }
2290         
2291         if (this.align === 'right') {
2292             cfg.cls += ' navbar-right';
2293         }
2294         
2295         if (this.inverse) {
2296             cfg.cls += ' navbar-inverse';
2297             
2298         }
2299         
2300         
2301         return cfg;
2302     }
2303    
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * row
2312  * 
2313  */
2314
2315 /**
2316  * @class Roo.bootstrap.Navbar.Item
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap Navbar.Button class
2319  * @cfg {String} href  link to
2320  * @cfg {String} html content of button
2321  * @cfg {String} badge text inside badge
2322  * @cfg {String} glyphicon name of glyphicon
2323  * @cfg {String} icon name of font awesome icon
2324  * @cfg {Boolena} active Is item active
2325  * @cfg {Boolean} preventDefault (true | false) default false
2326   
2327  * @constructor
2328  * Create a new Navbar Button
2329  * @param {Object} config The config object
2330  */
2331 Roo.bootstrap.Navbar.Item = function(config){
2332     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2333     this.addEvents({
2334         // raw events
2335         /**
2336          * @event click
2337          * The raw click event for the entire grid.
2338          * @param {Roo.EventObject} e
2339          */
2340         "click" : true
2341     });
2342 };
2343
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2345     
2346     href: false,
2347     html: '',
2348     badge: '',
2349     icon: false,
2350     glyphicon: false,
2351     icon: false,
2352     active: false,
2353     preventDefault : false,
2354     
2355     getAutoCreate : function(){
2356         
2357         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2358         
2359         if (this.parent().parent().sidebar === true) {
2360             cfg = {
2361                 tag: 'li',
2362                 cls: '',
2363                 cn: [
2364                     {
2365                         tag: 'p',
2366                         cls: ''
2367                     }
2368                 ]
2369             }
2370             
2371             if (this.html) {
2372                 cfg.cn[0].html = this.html;
2373             }
2374             
2375             if (this.active) {
2376                 this.cls += ' active';
2377             }
2378             
2379             if (this.menu) {
2380                 cfg.cn[0].cls += ' dropdown-toggle';
2381                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2382             }
2383             
2384             if (this.href) {
2385                 cfg.cn[0].tag = 'a',
2386                 cfg.cn[0].href = this.href;
2387             }
2388             
2389             if (this.glyphicon) {
2390                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2391             }
2392             
2393             if (this.icon) {
2394                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2395             }
2396             
2397             return cfg;
2398         }
2399         
2400         cfg = {
2401             tag: 'li',
2402             cls: 'nav-item'
2403         }
2404         
2405         if (this.active) {
2406             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2407         }
2408             
2409         cfg.cn = [
2410             {
2411                 tag: 'p',
2412                 html: 'Text'
2413             }
2414         ];
2415         
2416         if (this.glyphicon) {
2417             if(cfg.html){cfg.html = ' ' + this.html};
2418             cfg.cn=[
2419                 {
2420                     tag: 'span',
2421                     cls: 'glyphicon glyphicon-' + this.glyphicon
2422                 }
2423             ];
2424         }
2425         
2426         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2427         
2428         if (this.menu) {
2429             cfg.cn[0].tag='a';
2430             cfg.cn[0].href='#';
2431             cfg.cn[0].html += " <span class='caret'></span>";
2432         //}else if (!this.href) {
2433         //    cfg.cn[0].tag='p';
2434         //    cfg.cn[0].cls='navbar-text';
2435         } else {
2436             cfg.cn[0].tag='a';
2437             cfg.cn[0].href=this.href||'#';
2438             cfg.cn[0].html=this.html;
2439         }
2440         
2441         if (this.badge !== '') {
2442             
2443             cfg.cn[0].cn=[
2444                 cfg.cn[0].html + ' ',
2445                 {
2446                     tag: 'span',
2447                     cls: 'badge',
2448                     html: this.badge
2449                 }
2450             ];
2451             cfg.cn[0].html=''
2452         }
2453          
2454         if (this.icon) {
2455             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2456         }
2457         
2458         return cfg;
2459     },
2460     initEvents: function() {
2461        // Roo.log('init events?');
2462        // Roo.log(this.el.dom);
2463         this.el.select('a',true).on('click', this.onClick, this);
2464     },
2465     
2466     onClick : function(e)
2467     {
2468         if(this.preventDefault){
2469             e.preventDefault();
2470         }
2471         
2472         if(this.fireEvent('click', this, e) === false){
2473             return;
2474         };
2475         
2476         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477             this.onTabsClick(e);
2478         } 
2479     },
2480     
2481     onTabsClick : function(e)
2482     {
2483         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484             v.removeClass('active');
2485         })
2486
2487         this.el.addClass('active');
2488
2489         if(this.href && this.href.substring(0,1) == '#'){
2490             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2491
2492             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493                 v.removeClass('active');
2494             });
2495
2496             tab.addClass('active');
2497         }
2498     }
2499    
2500 });
2501  
2502
2503  /*
2504  * - LGPL
2505  *
2506  * row
2507  * 
2508  */
2509
2510 /**
2511  * @class Roo.bootstrap.Row
2512  * @extends Roo.bootstrap.Component
2513  * Bootstrap Row class (contains columns...)
2514  * 
2515  * @constructor
2516  * Create a new Row
2517  * @param {Object} config The config object
2518  */
2519
2520 Roo.bootstrap.Row = function(config){
2521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2525     
2526     getAutoCreate : function(){
2527        return {
2528             cls: 'row clearfix'
2529        };
2530     }
2531     
2532     
2533 });
2534
2535  
2536
2537  /*
2538  * - LGPL
2539  *
2540  * element
2541  * 
2542  */
2543
2544 /**
2545  * @class Roo.bootstrap.Element
2546  * @extends Roo.bootstrap.Component
2547  * Bootstrap Element class
2548  * @cfg {String} html contents of the element
2549  * @cfg {String} tag tag of the element
2550  * @cfg {String} cls class of the element
2551  * 
2552  * @constructor
2553  * Create a new Element
2554  * @param {Object} config The config object
2555  */
2556
2557 Roo.bootstrap.Element = function(config){
2558     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2559 };
2560
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2562     
2563     tag: 'div',
2564     cls: '',
2565     html: '',
2566      
2567     
2568     getAutoCreate : function(){
2569         
2570         var cfg = {
2571             tag: this.tag,
2572             cls: this.cls,
2573             html: this.html
2574         }
2575         
2576         
2577         
2578         return cfg;
2579     }
2580    
2581 });
2582
2583  
2584
2585  /*
2586  * - LGPL
2587  *
2588  * pagination
2589  * 
2590  */
2591
2592 /**
2593  * @class Roo.bootstrap.Pagination
2594  * @extends Roo.bootstrap.Component
2595  * Bootstrap Pagination class
2596  * @cfg {String} size xs | sm | md | lg
2597  * @cfg {Boolean} inverse false | true
2598  * 
2599  * @constructor
2600  * Create a new Pagination
2601  * @param {Object} config The config object
2602  */
2603
2604 Roo.bootstrap.Pagination = function(config){
2605     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2609     
2610     cls: false,
2611     size: false,
2612     inverse: false,
2613     
2614     getAutoCreate : function(){
2615         var cfg = {
2616             tag: 'ul',
2617                 cls: 'pagination'
2618         };
2619         if (this.inverse) {
2620             cfg.cls += ' inverse';
2621         }
2622         if (this.html) {
2623             cfg.html=this.html;
2624         }
2625         if (this.cls) {
2626             cfg.cls += " " + this.cls;
2627         }
2628         return cfg;
2629     }
2630    
2631 });
2632
2633  
2634
2635  /*
2636  * - LGPL
2637  *
2638  * Pagination item
2639  * 
2640  */
2641
2642
2643 /**
2644  * @class Roo.bootstrap.PaginationItem
2645  * @extends Roo.bootstrap.Component
2646  * Bootstrap PaginationItem class
2647  * @cfg {String} html text
2648  * @cfg {String} href the link
2649  * @cfg {Boolean} preventDefault (true | false) default true
2650  * @cfg {Boolean} active (true | false) default false
2651  * 
2652  * 
2653  * @constructor
2654  * Create a new PaginationItem
2655  * @param {Object} config The config object
2656  */
2657
2658
2659 Roo.bootstrap.PaginationItem = function(config){
2660     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2661     this.addEvents({
2662         // raw events
2663         /**
2664          * @event click
2665          * The raw click event for the entire grid.
2666          * @param {Roo.EventObject} e
2667          */
2668         "click" : true
2669     });
2670 };
2671
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2673     
2674     href : false,
2675     html : false,
2676     preventDefault: true,
2677     active : false,
2678     cls : false,
2679     
2680     getAutoCreate : function(){
2681         var cfg= {
2682             tag: 'li',
2683             cn: [
2684                 {
2685                     tag : 'a',
2686                     href : this.href ? this.href : '#',
2687                     html : this.html ? this.html : ''
2688                 }
2689             ]
2690         };
2691         
2692         if(this.cls){
2693             cfg.cls = this.cls;
2694         }
2695         
2696         if(this.active){
2697             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2698         }
2699         
2700         return cfg;
2701     },
2702     
2703     initEvents: function() {
2704         
2705         this.el.on('click', this.onClick, this);
2706         
2707     },
2708     onClick : function(e)
2709     {
2710         Roo.log('PaginationItem on click ');
2711         if(this.preventDefault){
2712             e.preventDefault();
2713         }
2714         
2715         this.fireEvent('click', this, e);
2716     }
2717    
2718 });
2719
2720  
2721
2722  /*
2723  * - LGPL
2724  *
2725  * slider
2726  * 
2727  */
2728
2729
2730 /**
2731  * @class Roo.bootstrap.Slider
2732  * @extends Roo.bootstrap.Component
2733  * Bootstrap Slider class
2734  *    
2735  * @constructor
2736  * Create a new Slider
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.Slider = function(config){
2741     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2745     
2746     getAutoCreate : function(){
2747         
2748         var cfg = {
2749             tag: 'div',
2750             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2751             cn: [
2752                 {
2753                     tag: 'a',
2754                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2755                 }
2756             ]
2757         }
2758         
2759         return cfg;
2760     }
2761    
2762 });
2763
2764  /*
2765  * - LGPL
2766  *
2767  * table
2768  * 
2769  */
2770
2771 /**
2772  * @class Roo.bootstrap.Table
2773  * @extends Roo.bootstrap.Component
2774  * Bootstrap Table class
2775  * @cfg {String} cls table class
2776  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777  * @cfg {String} bgcolor Specifies the background color for a table
2778  * @cfg {Number} border Specifies whether the table cells should have borders or not
2779  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780  * @cfg {Number} cellspacing Specifies the space between cells
2781  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783  * @cfg {String} sortable Specifies that the table should be sortable
2784  * @cfg {String} summary Specifies a summary of the content of a table
2785  * @cfg {Number} width Specifies the width of a table
2786  * 
2787  * @cfg {boolean} striped Should the rows be alternative striped
2788  * @cfg {boolean} bordered Add borders to the table
2789  * @cfg {boolean} hover Add hover highlighting
2790  * @cfg {boolean} condensed Format condensed
2791  * @cfg {boolean} responsive Format condensed
2792  *
2793  
2794  
2795  * 
2796  * @constructor
2797  * Create a new Table
2798  * @param {Object} config The config object
2799  */
2800
2801 Roo.bootstrap.Table = function(config){
2802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2803     
2804     if (this.sm) {
2805         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806         this.sm = this.selModel;
2807         this.sm.xmodule = this.xmodule || false;
2808     }
2809     if (this.cm && typeof(this.cm.config) == 'undefined') {
2810         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811         this.cm = this.colModel;
2812         this.cm.xmodule = this.xmodule || false;
2813     }
2814     if (this.store) {
2815         this.store= Roo.factory(this.store, Roo.data);
2816         this.ds = this.store;
2817         this.ds.xmodule = this.xmodule || false;
2818          
2819     }
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2823     
2824     cls: false,
2825     align: false,
2826     bgcolor: false,
2827     border: false,
2828     cellpadding: false,
2829     cellspacing: false,
2830     frame: false,
2831     rules: false,
2832     sortable: false,
2833     summary: false,
2834     width: false,
2835     striped : false,
2836     bordered: false,
2837     hover:  false,
2838     condensed : false,
2839     responsive : false,
2840     sm : false,
2841     cm : false,
2842     store : false,
2843     
2844     getAutoCreate : function(){
2845         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2846         
2847         cfg = {
2848             tag: 'table',
2849             cls : 'table',
2850             cn : []
2851         }
2852             
2853         if (this.striped) {
2854             cfg.cls += ' table-striped';
2855         }
2856         if (this.hover) {
2857             cfg.cls += ' table-hover';
2858         }
2859         if (this.bordered) {
2860             cfg.cls += ' table-bordered';
2861         }
2862         if (this.condensed) {
2863             cfg.cls += ' table-condensed';
2864         }
2865         if (this.responsive) {
2866             cfg.cls += ' table-responsive';
2867         }
2868         
2869           
2870         
2871         
2872         if (this.cls) {
2873             cfg.cls+=  ' ' +this.cls;
2874         }
2875         
2876         // this lot should be simplifed...
2877         
2878         if (this.align) {
2879             cfg.align=this.align;
2880         }
2881         if (this.bgcolor) {
2882             cfg.bgcolor=this.bgcolor;
2883         }
2884         if (this.border) {
2885             cfg.border=this.border;
2886         }
2887         if (this.cellpadding) {
2888             cfg.cellpadding=this.cellpadding;
2889         }
2890         if (this.cellspacing) {
2891             cfg.cellspacing=this.cellspacing;
2892         }
2893         if (this.frame) {
2894             cfg.frame=this.frame;
2895         }
2896         if (this.rules) {
2897             cfg.rules=this.rules;
2898         }
2899         if (this.sortable) {
2900             cfg.sortable=this.sortable;
2901         }
2902         if (this.summary) {
2903             cfg.summary=this.summary;
2904         }
2905         if (this.width) {
2906             cfg.width=this.width;
2907         }
2908         
2909         if(this.store || this.cm){
2910             cfg.cn.push(this.renderHeader());
2911             cfg.cn.push(this.renderBody());
2912             cfg.cn.push(this.renderFooter());
2913             
2914             cfg.cls+=  ' TableGrid';
2915         }
2916         
2917         return cfg;
2918     },
2919 //    
2920 //    initTableGrid : function()
2921 //    {
2922 //        var cfg = {};
2923 //        
2924 //        var header = {
2925 //            tag: 'thead',
2926 //            cn : []
2927 //        };
2928 //        
2929 //        var cm = this.cm;
2930 //        
2931 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2932 //            header.cn.push({
2933 //                tag: 'th',
2934 //                html: cm.getColumnHeader(i)
2935 //            })
2936 //        }
2937 //        
2938 //        cfg.push(header);
2939 //        
2940 //        return cfg;
2941 //        
2942 //        
2943 //    },
2944     
2945     initEvents : function()
2946     {   
2947         if(!this.store || !this.cm){
2948             return;
2949         }
2950         
2951         Roo.log('initEvents with ds!!!!');
2952         
2953         var _this = this;
2954         
2955         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
2956             e.on('click', _this.sort, _this);
2957         });
2958 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2959 //        this.maskEl.enableDisplayMode("block");
2960 //        this.maskEl.show();
2961         
2962         this.store.on('load', this.onLoad, this);
2963         this.store.on('beforeload', this.onBeforeLoad, this);
2964         
2965         this.store.load();
2966         
2967         
2968         
2969     },
2970     
2971     sort : function(e,el)
2972     {
2973         var col = Roo.get(el)
2974         
2975         if(!col.hasClass('sortable')){
2976             return;
2977         }
2978         
2979         var sort = col.attr('sort');
2980         var dir = 'ASC';
2981         
2982         if(col.hasClass('glyphicon-arrow-up')){
2983             dir = 'DESC';
2984         }
2985         
2986         this.store.sortInfo = {field : sort, direction : dir};
2987         
2988         this.store.load();
2989     },
2990     
2991     renderHeader : function()
2992     {
2993         var header = {
2994             tag: 'thead',
2995             cn : []
2996         };
2997         
2998         var cm = this.cm;
2999         
3000         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3001             
3002             var config = cm.config[i];
3003             
3004             var c = {
3005                 tag: 'th',
3006                 html: cm.getColumnHeader(i)
3007             };
3008             
3009             if(typeof(config.dataIndex) != 'undefined'){
3010                 c.sort = config.dataIndex;
3011             }
3012             
3013             if(typeof(config.sortable) != 'undefined' && config.sortable){
3014                 c.cls = 'sortable';
3015             }
3016             
3017             header.cn.push(c)
3018         }
3019         
3020         return header;
3021     },
3022     
3023     renderBody : function()
3024     {
3025         var body = {
3026             tag: 'tbody',
3027             cn : []
3028         };
3029         
3030         return body;
3031     },
3032     
3033     renderFooter : function()
3034     {
3035         var footer = {
3036             tag: 'tfoot',
3037             cn : []
3038         };
3039         
3040         return footer;
3041     },
3042     
3043     onLoad : function()
3044     {
3045         Roo.log('ds onload');
3046         
3047         var _this = this;
3048         var cm = this.cm;
3049         
3050         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3051             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3052             
3053             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3054                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3055                 return;
3056             }
3057             
3058             e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3059         });
3060         
3061         var tbody = this.el.select('tbody', true).first();
3062         
3063         var renders = [];
3064         
3065         if(this.store.getCount() > 0){
3066             this.store.data.each(function(d){
3067                 var row = {
3068                     tag : 'tr',
3069                     cn : []
3070                 };
3071                 
3072                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3073                     var renderer = cm.getRenderer(i);
3074                     var config = cm.config[i];
3075                     var value = '';
3076                     var id = Roo.id();
3077                     
3078                     if(typeof(renderer) !== 'undefined'){
3079                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3080                     }
3081                     
3082                     if(typeof(value) === 'object'){
3083                         renders.push({
3084                             id : id,
3085                             cfg : value 
3086                         })
3087                     }
3088                     
3089                     var td = {
3090                         tag: 'td',
3091                         id: id,
3092                         html: (typeof(value) === 'object') ? '' : value
3093                     };
3094                     
3095                     if(typeof(config.width) != 'undefined'){
3096                         td.width = config.width;
3097                     }
3098                     
3099                     row.cn.push(td);
3100                    
3101                 }
3102                 
3103                 tbody.createChild(row);
3104                 
3105             });
3106         }
3107         
3108         
3109         if(renders.length){
3110             var _this = this;
3111             Roo.each(renders, function(r){
3112                 _this.renderColumn(r);
3113             })
3114         }
3115 //        
3116 //        if(this.loadMask){
3117 //            this.maskEl.hide();
3118 //        }
3119     },
3120     
3121     onBeforeLoad : function()
3122     {
3123         Roo.log('ds onBeforeLoad');
3124         
3125         this.clear();
3126         
3127 //        if(this.loadMask){
3128 //            this.maskEl.show();
3129 //        }
3130     },
3131     
3132     clear : function()
3133     {
3134         this.el.select('tbody', true).first().dom.innerHTML = '';
3135     },
3136     
3137     getSelectionModel : function(){
3138         if(!this.selModel){
3139             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3140         }
3141         return this.selModel;
3142     },
3143     
3144     renderColumn : function(r)
3145     {
3146         var _this = this;
3147         r.cfg.render(Roo.get(r.id));
3148         
3149         if(r.cfg.cn){
3150             Roo.each(r.cfg.cn, function(c){
3151                 var child = {
3152                     id: r.id,
3153                     cfg: c
3154                 }
3155                 _this.renderColumn(child);
3156             })
3157         }
3158     }
3159    
3160 });
3161
3162  
3163
3164  /*
3165  * - LGPL
3166  *
3167  * table cell
3168  * 
3169  */
3170
3171 /**
3172  * @class Roo.bootstrap.TableCell
3173  * @extends Roo.bootstrap.Component
3174  * Bootstrap TableCell class
3175  * @cfg {String} html cell contain text
3176  * @cfg {String} cls cell class
3177  * @cfg {String} tag cell tag (td|th) default td
3178  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3179  * @cfg {String} align Aligns the content in a cell
3180  * @cfg {String} axis Categorizes cells
3181  * @cfg {String} bgcolor Specifies the background color of a cell
3182  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3183  * @cfg {Number} colspan Specifies the number of columns a cell should span
3184  * @cfg {String} headers Specifies one or more header cells a cell is related to
3185  * @cfg {Number} height Sets the height of a cell
3186  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3187  * @cfg {Number} rowspan Sets the number of rows a cell should span
3188  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3189  * @cfg {String} valign Vertical aligns the content in a cell
3190  * @cfg {Number} width Specifies the width of a cell
3191  * 
3192  * @constructor
3193  * Create a new TableCell
3194  * @param {Object} config The config object
3195  */
3196
3197 Roo.bootstrap.TableCell = function(config){
3198     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3199 };
3200
3201 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3202     
3203     html: false,
3204     cls: false,
3205     tag: false,
3206     abbr: false,
3207     align: false,
3208     axis: false,
3209     bgcolor: false,
3210     charoff: false,
3211     colspan: false,
3212     headers: false,
3213     height: false,
3214     nowrap: false,
3215     rowspan: false,
3216     scope: false,
3217     valign: false,
3218     width: false,
3219     
3220     
3221     getAutoCreate : function(){
3222         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3223         
3224         cfg = {
3225             tag: 'td'
3226         }
3227         
3228         if(this.tag){
3229             cfg.tag = this.tag;
3230         }
3231         
3232         if (this.html) {
3233             cfg.html=this.html
3234         }
3235         if (this.cls) {
3236             cfg.cls=this.cls
3237         }
3238         if (this.abbr) {
3239             cfg.abbr=this.abbr
3240         }
3241         if (this.align) {
3242             cfg.align=this.align
3243         }
3244         if (this.axis) {
3245             cfg.axis=this.axis
3246         }
3247         if (this.bgcolor) {
3248             cfg.bgcolor=this.bgcolor
3249         }
3250         if (this.charoff) {
3251             cfg.charoff=this.charoff
3252         }
3253         if (this.colspan) {
3254             cfg.colspan=this.colspan
3255         }
3256         if (this.headers) {
3257             cfg.headers=this.headers
3258         }
3259         if (this.height) {
3260             cfg.height=this.height
3261         }
3262         if (this.nowrap) {
3263             cfg.nowrap=this.nowrap
3264         }
3265         if (this.rowspan) {
3266             cfg.rowspan=this.rowspan
3267         }
3268         if (this.scope) {
3269             cfg.scope=this.scope
3270         }
3271         if (this.valign) {
3272             cfg.valign=this.valign
3273         }
3274         if (this.width) {
3275             cfg.width=this.width
3276         }
3277         
3278         
3279         return cfg;
3280     }
3281    
3282 });
3283
3284  
3285
3286  /*
3287  * - LGPL
3288  *
3289  * table row
3290  * 
3291  */
3292
3293 /**
3294  * @class Roo.bootstrap.TableRow
3295  * @extends Roo.bootstrap.Component
3296  * Bootstrap TableRow class
3297  * @cfg {String} cls row class
3298  * @cfg {String} align Aligns the content in a table row
3299  * @cfg {String} bgcolor Specifies a background color for a table row
3300  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3301  * @cfg {String} valign Vertical aligns the content in a table row
3302  * 
3303  * @constructor
3304  * Create a new TableRow
3305  * @param {Object} config The config object
3306  */
3307
3308 Roo.bootstrap.TableRow = function(config){
3309     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3310 };
3311
3312 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3313     
3314     cls: false,
3315     align: false,
3316     bgcolor: false,
3317     charoff: false,
3318     valign: false,
3319     
3320     getAutoCreate : function(){
3321         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3322         
3323         cfg = {
3324             tag: 'tr'
3325         }
3326             
3327         if(this.cls){
3328             cfg.cls = this.cls;
3329         }
3330         if(this.align){
3331             cfg.align = this.align;
3332         }
3333         if(this.bgcolor){
3334             cfg.bgcolor = this.bgcolor;
3335         }
3336         if(this.charoff){
3337             cfg.charoff = this.charoff;
3338         }
3339         if(this.valign){
3340             cfg.valign = this.valign;
3341         }
3342         
3343         return cfg;
3344     }
3345    
3346 });
3347
3348  
3349
3350  /*
3351  * - LGPL
3352  *
3353  * table body
3354  * 
3355  */
3356
3357 /**
3358  * @class Roo.bootstrap.TableBody
3359  * @extends Roo.bootstrap.Component
3360  * Bootstrap TableBody class
3361  * @cfg {String} cls element class
3362  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3363  * @cfg {String} align Aligns the content inside the element
3364  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3365  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3366  * 
3367  * @constructor
3368  * Create a new TableBody
3369  * @param {Object} config The config object
3370  */
3371
3372 Roo.bootstrap.TableBody = function(config){
3373     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3374 };
3375
3376 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3377     
3378     cls: false,
3379     tag: false,
3380     align: false,
3381     charoff: false,
3382     valign: false,
3383     
3384     getAutoCreate : function(){
3385         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3386         
3387         cfg = {
3388             tag: 'tbody'
3389         }
3390             
3391         if (this.cls) {
3392             cfg.cls=this.cls
3393         }
3394         if(this.tag){
3395             cfg.tag = this.tag;
3396         }
3397         
3398         if(this.align){
3399             cfg.align = this.align;
3400         }
3401         if(this.charoff){
3402             cfg.charoff = this.charoff;
3403         }
3404         if(this.valign){
3405             cfg.valign = this.valign;
3406         }
3407         
3408         return cfg;
3409     }
3410     
3411     
3412 //    initEvents : function()
3413 //    {
3414 //        
3415 //        if(!this.store){
3416 //            return;
3417 //        }
3418 //        
3419 //        this.store = Roo.factory(this.store, Roo.data);
3420 //        this.store.on('load', this.onLoad, this);
3421 //        
3422 //        this.store.load();
3423 //        
3424 //    },
3425 //    
3426 //    onLoad: function () 
3427 //    {   
3428 //        this.fireEvent('load', this);
3429 //    }
3430 //    
3431 //   
3432 });
3433
3434  
3435
3436  /*
3437  * Based on:
3438  * Ext JS Library 1.1.1
3439  * Copyright(c) 2006-2007, Ext JS, LLC.
3440  *
3441  * Originally Released Under LGPL - original licence link has changed is not relivant.
3442  *
3443  * Fork - LGPL
3444  * <script type="text/javascript">
3445  */
3446
3447 // as we use this in bootstrap.
3448 Roo.namespace('Roo.form');
3449  /**
3450  * @class Roo.form.Action
3451  * Internal Class used to handle form actions
3452  * @constructor
3453  * @param {Roo.form.BasicForm} el The form element or its id
3454  * @param {Object} config Configuration options
3455  */
3456
3457  
3458  
3459 // define the action interface
3460 Roo.form.Action = function(form, options){
3461     this.form = form;
3462     this.options = options || {};
3463 };
3464 /**
3465  * Client Validation Failed
3466  * @const 
3467  */
3468 Roo.form.Action.CLIENT_INVALID = 'client';
3469 /**
3470  * Server Validation Failed
3471  * @const 
3472  */
3473 Roo.form.Action.SERVER_INVALID = 'server';
3474  /**
3475  * Connect to Server Failed
3476  * @const 
3477  */
3478 Roo.form.Action.CONNECT_FAILURE = 'connect';
3479 /**
3480  * Reading Data from Server Failed
3481  * @const 
3482  */
3483 Roo.form.Action.LOAD_FAILURE = 'load';
3484
3485 Roo.form.Action.prototype = {
3486     type : 'default',
3487     failureType : undefined,
3488     response : undefined,
3489     result : undefined,
3490
3491     // interface method
3492     run : function(options){
3493
3494     },
3495
3496     // interface method
3497     success : function(response){
3498
3499     },
3500
3501     // interface method
3502     handleResponse : function(response){
3503
3504     },
3505
3506     // default connection failure
3507     failure : function(response){
3508         
3509         this.response = response;
3510         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3511         this.form.afterAction(this, false);
3512     },
3513
3514     processResponse : function(response){
3515         this.response = response;
3516         if(!response.responseText){
3517             return true;
3518         }
3519         this.result = this.handleResponse(response);
3520         return this.result;
3521     },
3522
3523     // utility functions used internally
3524     getUrl : function(appendParams){
3525         var url = this.options.url || this.form.url || this.form.el.dom.action;
3526         if(appendParams){
3527             var p = this.getParams();
3528             if(p){
3529                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3530             }
3531         }
3532         return url;
3533     },
3534
3535     getMethod : function(){
3536         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3537     },
3538
3539     getParams : function(){
3540         var bp = this.form.baseParams;
3541         var p = this.options.params;
3542         if(p){
3543             if(typeof p == "object"){
3544                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3545             }else if(typeof p == 'string' && bp){
3546                 p += '&' + Roo.urlEncode(bp);
3547             }
3548         }else if(bp){
3549             p = Roo.urlEncode(bp);
3550         }
3551         return p;
3552     },
3553
3554     createCallback : function(){
3555         return {
3556             success: this.success,
3557             failure: this.failure,
3558             scope: this,
3559             timeout: (this.form.timeout*1000),
3560             upload: this.form.fileUpload ? this.success : undefined
3561         };
3562     }
3563 };
3564
3565 Roo.form.Action.Submit = function(form, options){
3566     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3567 };
3568
3569 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3570     type : 'submit',
3571
3572     haveProgress : false,
3573     uploadComplete : false,
3574     
3575     // uploadProgress indicator.
3576     uploadProgress : function()
3577     {
3578         if (!this.form.progressUrl) {
3579             return;
3580         }
3581         
3582         if (!this.haveProgress) {
3583             Roo.MessageBox.progress("Uploading", "Uploading");
3584         }
3585         if (this.uploadComplete) {
3586            Roo.MessageBox.hide();
3587            return;
3588         }
3589         
3590         this.haveProgress = true;
3591    
3592         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3593         
3594         var c = new Roo.data.Connection();
3595         c.request({
3596             url : this.form.progressUrl,
3597             params: {
3598                 id : uid
3599             },
3600             method: 'GET',
3601             success : function(req){
3602                //console.log(data);
3603                 var rdata = false;
3604                 var edata;
3605                 try  {
3606                    rdata = Roo.decode(req.responseText)
3607                 } catch (e) {
3608                     Roo.log("Invalid data from server..");
3609                     Roo.log(edata);
3610                     return;
3611                 }
3612                 if (!rdata || !rdata.success) {
3613                     Roo.log(rdata);
3614                     Roo.MessageBox.alert(Roo.encode(rdata));
3615                     return;
3616                 }
3617                 var data = rdata.data;
3618                 
3619                 if (this.uploadComplete) {
3620                    Roo.MessageBox.hide();
3621                    return;
3622                 }
3623                    
3624                 if (data){
3625                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3626                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3627                     );
3628                 }
3629                 this.uploadProgress.defer(2000,this);
3630             },
3631        
3632             failure: function(data) {
3633                 Roo.log('progress url failed ');
3634                 Roo.log(data);
3635             },
3636             scope : this
3637         });
3638            
3639     },
3640     
3641     
3642     run : function()
3643     {
3644         // run get Values on the form, so it syncs any secondary forms.
3645         this.form.getValues();
3646         
3647         var o = this.options;
3648         var method = this.getMethod();
3649         var isPost = method == 'POST';
3650         if(o.clientValidation === false || this.form.isValid()){
3651             
3652             if (this.form.progressUrl) {
3653                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3654                     (new Date() * 1) + '' + Math.random());
3655                     
3656             } 
3657             
3658             
3659             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3660                 form:this.form.el.dom,
3661                 url:this.getUrl(!isPost),
3662                 method: method,
3663                 params:isPost ? this.getParams() : null,
3664                 isUpload: this.form.fileUpload
3665             }));
3666             
3667             this.uploadProgress();
3668
3669         }else if (o.clientValidation !== false){ // client validation failed
3670             this.failureType = Roo.form.Action.CLIENT_INVALID;
3671             this.form.afterAction(this, false);
3672         }
3673     },
3674
3675     success : function(response)
3676     {
3677         this.uploadComplete= true;
3678         if (this.haveProgress) {
3679             Roo.MessageBox.hide();
3680         }
3681         
3682         
3683         var result = this.processResponse(response);
3684         if(result === true || result.success){
3685             this.form.afterAction(this, true);
3686             return;
3687         }
3688         if(result.errors){
3689             this.form.markInvalid(result.errors);
3690             this.failureType = Roo.form.Action.SERVER_INVALID;
3691         }
3692         this.form.afterAction(this, false);
3693     },
3694     failure : function(response)
3695     {
3696         this.uploadComplete= true;
3697         if (this.haveProgress) {
3698             Roo.MessageBox.hide();
3699         }
3700         
3701         this.response = response;
3702         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3703         this.form.afterAction(this, false);
3704     },
3705     
3706     handleResponse : function(response){
3707         if(this.form.errorReader){
3708             var rs = this.form.errorReader.read(response);
3709             var errors = [];
3710             if(rs.records){
3711                 for(var i = 0, len = rs.records.length; i < len; i++) {
3712                     var r = rs.records[i];
3713                     errors[i] = r.data;
3714                 }
3715             }
3716             if(errors.length < 1){
3717                 errors = null;
3718             }
3719             return {
3720                 success : rs.success,
3721                 errors : errors
3722             };
3723         }
3724         var ret = false;
3725         try {
3726             ret = Roo.decode(response.responseText);
3727         } catch (e) {
3728             ret = {
3729                 success: false,
3730                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3731                 errors : []
3732             };
3733         }
3734         return ret;
3735         
3736     }
3737 });
3738
3739
3740 Roo.form.Action.Load = function(form, options){
3741     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3742     this.reader = this.form.reader;
3743 };
3744
3745 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3746     type : 'load',
3747
3748     run : function(){
3749         
3750         Roo.Ajax.request(Roo.apply(
3751                 this.createCallback(), {
3752                     method:this.getMethod(),
3753                     url:this.getUrl(false),
3754                     params:this.getParams()
3755         }));
3756     },
3757
3758     success : function(response){
3759         
3760         var result = this.processResponse(response);
3761         if(result === true || !result.success || !result.data){
3762             this.failureType = Roo.form.Action.LOAD_FAILURE;
3763             this.form.afterAction(this, false);
3764             return;
3765         }
3766         this.form.clearInvalid();
3767         this.form.setValues(result.data);
3768         this.form.afterAction(this, true);
3769     },
3770
3771     handleResponse : function(response){
3772         if(this.form.reader){
3773             var rs = this.form.reader.read(response);
3774             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3775             return {
3776                 success : rs.success,
3777                 data : data
3778             };
3779         }
3780         return Roo.decode(response.responseText);
3781     }
3782 });
3783
3784 Roo.form.Action.ACTION_TYPES = {
3785     'load' : Roo.form.Action.Load,
3786     'submit' : Roo.form.Action.Submit
3787 };/*
3788  * - LGPL
3789  *
3790  * form
3791  * 
3792  */
3793
3794 /**
3795  * @class Roo.bootstrap.Form
3796  * @extends Roo.bootstrap.Component
3797  * Bootstrap Form class
3798  * @cfg {String} method  GET | POST (default POST)
3799  * @cfg {String} labelAlign top | left (default top)
3800   * @cfg {String} align left  | right - for navbars
3801
3802  * 
3803  * @constructor
3804  * Create a new Form
3805  * @param {Object} config The config object
3806  */
3807
3808
3809 Roo.bootstrap.Form = function(config){
3810     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3811     this.addEvents({
3812         /**
3813          * @event clientvalidation
3814          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3815          * @param {Form} this
3816          * @param {Boolean} valid true if the form has passed client-side validation
3817          */
3818         clientvalidation: true,
3819         /**
3820          * @event beforeaction
3821          * Fires before any action is performed. Return false to cancel the action.
3822          * @param {Form} this
3823          * @param {Action} action The action to be performed
3824          */
3825         beforeaction: true,
3826         /**
3827          * @event actionfailed
3828          * Fires when an action fails.
3829          * @param {Form} this
3830          * @param {Action} action The action that failed
3831          */
3832         actionfailed : true,
3833         /**
3834          * @event actioncomplete
3835          * Fires when an action is completed.
3836          * @param {Form} this
3837          * @param {Action} action The action that completed
3838          */
3839         actioncomplete : true
3840     });
3841     
3842 };
3843
3844 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3845       
3846      /**
3847      * @cfg {String} method
3848      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3849      */
3850     method : 'POST',
3851     /**
3852      * @cfg {String} url
3853      * The URL to use for form actions if one isn't supplied in the action options.
3854      */
3855     /**
3856      * @cfg {Boolean} fileUpload
3857      * Set to true if this form is a file upload.
3858      */
3859      
3860     /**
3861      * @cfg {Object} baseParams
3862      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3863      */
3864       
3865     /**
3866      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3867      */
3868     timeout: 30,
3869     /**
3870      * @cfg {Sting} align (left|right) for navbar forms
3871      */
3872     align : 'left',
3873
3874     // private
3875     activeAction : null,
3876  
3877     /**
3878      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3879      * element by passing it or its id or mask the form itself by passing in true.
3880      * @type Mixed
3881      */
3882     waitMsgTarget : false,
3883     
3884      
3885     
3886     /**
3887      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3888      * element by passing it or its id or mask the form itself by passing in true.
3889      * @type Mixed
3890      */
3891     
3892     getAutoCreate : function(){
3893         
3894         var cfg = {
3895             tag: 'form',
3896             method : this.method || 'POST',
3897             id : this.id || Roo.id(),
3898             cls : ''
3899         }
3900         if (this.parent().xtype.match(/^Nav/)) {
3901             cfg.cls = 'navbar-form navbar-' + this.align;
3902             
3903         }
3904         
3905         if (this.labelAlign == 'left' ) {
3906             cfg.cls += ' form-horizontal';
3907         }
3908         
3909         
3910         return cfg;
3911     },
3912     initEvents : function()
3913     {
3914         this.el.on('submit', this.onSubmit, this);
3915         
3916         
3917     },
3918     // private
3919     onSubmit : function(e){
3920         e.stopEvent();
3921     },
3922     
3923      /**
3924      * Returns true if client-side validation on the form is successful.
3925      * @return Boolean
3926      */
3927     isValid : function(){
3928         var items = this.getItems();
3929         var valid = true;
3930         items.each(function(f){
3931            if(!f.validate()){
3932                valid = false;
3933                
3934            }
3935         });
3936         return valid;
3937     },
3938     /**
3939      * Returns true if any fields in this form have changed since their original load.
3940      * @return Boolean
3941      */
3942     isDirty : function(){
3943         var dirty = false;
3944         var items = this.getItems();
3945         items.each(function(f){
3946            if(f.isDirty()){
3947                dirty = true;
3948                return false;
3949            }
3950            return true;
3951         });
3952         return dirty;
3953     },
3954      /**
3955      * Performs a predefined action (submit or load) or custom actions you define on this form.
3956      * @param {String} actionName The name of the action type
3957      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3958      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3959      * accept other config options):
3960      * <pre>
3961 Property          Type             Description
3962 ----------------  ---------------  ----------------------------------------------------------------------------------
3963 url               String           The url for the action (defaults to the form's url)
3964 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3965 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3966 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3967                                    validate the form on the client (defaults to false)
3968      * </pre>
3969      * @return {BasicForm} this
3970      */
3971     doAction : function(action, options){
3972         if(typeof action == 'string'){
3973             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3974         }
3975         if(this.fireEvent('beforeaction', this, action) !== false){
3976             this.beforeAction(action);
3977             action.run.defer(100, action);
3978         }
3979         return this;
3980     },
3981     
3982     // private
3983     beforeAction : function(action){
3984         var o = action.options;
3985         
3986         // not really supported yet.. ??
3987         
3988         //if(this.waitMsgTarget === true){
3989             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3990         //}else if(this.waitMsgTarget){
3991         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3992         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3993         //}else {
3994         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3995        // }
3996          
3997     },
3998
3999     // private
4000     afterAction : function(action, success){
4001         this.activeAction = null;
4002         var o = action.options;
4003         
4004         //if(this.waitMsgTarget === true){
4005             this.el.unmask();
4006         //}else if(this.waitMsgTarget){
4007         //    this.waitMsgTarget.unmask();
4008         //}else{
4009         //    Roo.MessageBox.updateProgress(1);
4010         //    Roo.MessageBox.hide();
4011        // }
4012         // 
4013         if(success){
4014             if(o.reset){
4015                 this.reset();
4016             }
4017             Roo.callback(o.success, o.scope, [this, action]);
4018             this.fireEvent('actioncomplete', this, action);
4019             
4020         }else{
4021             
4022             // failure condition..
4023             // we have a scenario where updates need confirming.
4024             // eg. if a locking scenario exists..
4025             // we look for { errors : { needs_confirm : true }} in the response.
4026             if (
4027                 (typeof(action.result) != 'undefined')  &&
4028                 (typeof(action.result.errors) != 'undefined')  &&
4029                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4030            ){
4031                 var _t = this;
4032                 Roo.log("not supported yet");
4033                  /*
4034                 
4035                 Roo.MessageBox.confirm(
4036                     "Change requires confirmation",
4037                     action.result.errorMsg,
4038                     function(r) {
4039                         if (r != 'yes') {
4040                             return;
4041                         }
4042                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4043                     }
4044                     
4045                 );
4046                 */
4047                 
4048                 
4049                 return;
4050             }
4051             
4052             Roo.callback(o.failure, o.scope, [this, action]);
4053             // show an error message if no failed handler is set..
4054             if (!this.hasListener('actionfailed')) {
4055                 Roo.log("need to add dialog support");
4056                 /*
4057                 Roo.MessageBox.alert("Error",
4058                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4059                         action.result.errorMsg :
4060                         "Saving Failed, please check your entries or try again"
4061                 );
4062                 */
4063             }
4064             
4065             this.fireEvent('actionfailed', this, action);
4066         }
4067         
4068     },
4069     /**
4070      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4071      * @param {String} id The value to search for
4072      * @return Field
4073      */
4074     findField : function(id){
4075         var items = this.getItems();
4076         var field = items.get(id);
4077         if(!field){
4078              items.each(function(f){
4079                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4080                     field = f;
4081                     return false;
4082                 }
4083                 return true;
4084             });
4085         }
4086         return field || null;
4087     },
4088      /**
4089      * Mark fields in this form invalid in bulk.
4090      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4091      * @return {BasicForm} this
4092      */
4093     markInvalid : function(errors){
4094         if(errors instanceof Array){
4095             for(var i = 0, len = errors.length; i < len; i++){
4096                 var fieldError = errors[i];
4097                 var f = this.findField(fieldError.id);
4098                 if(f){
4099                     f.markInvalid(fieldError.msg);
4100                 }
4101             }
4102         }else{
4103             var field, id;
4104             for(id in errors){
4105                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4106                     field.markInvalid(errors[id]);
4107                 }
4108             }
4109         }
4110         //Roo.each(this.childForms || [], function (f) {
4111         //    f.markInvalid(errors);
4112         //});
4113         
4114         return this;
4115     },
4116
4117     /**
4118      * Set values for fields in this form in bulk.
4119      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4120      * @return {BasicForm} this
4121      */
4122     setValues : function(values){
4123         if(values instanceof Array){ // array of objects
4124             for(var i = 0, len = values.length; i < len; i++){
4125                 var v = values[i];
4126                 var f = this.findField(v.id);
4127                 if(f){
4128                     f.setValue(v.value);
4129                     if(this.trackResetOnLoad){
4130                         f.originalValue = f.getValue();
4131                     }
4132                 }
4133             }
4134         }else{ // object hash
4135             var field, id;
4136             for(id in values){
4137                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4138                     
4139                     if (field.setFromData && 
4140                         field.valueField && 
4141                         field.displayField &&
4142                         // combos' with local stores can 
4143                         // be queried via setValue()
4144                         // to set their value..
4145                         (field.store && !field.store.isLocal)
4146                         ) {
4147                         // it's a combo
4148                         var sd = { };
4149                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4150                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4151                         field.setFromData(sd);
4152                         
4153                     } else {
4154                         field.setValue(values[id]);
4155                     }
4156                     
4157                     
4158                     if(this.trackResetOnLoad){
4159                         field.originalValue = field.getValue();
4160                     }
4161                 }
4162             }
4163         }
4164          
4165         //Roo.each(this.childForms || [], function (f) {
4166         //    f.setValues(values);
4167         //});
4168                 
4169         return this;
4170     },
4171
4172     /**
4173      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4174      * they are returned as an array.
4175      * @param {Boolean} asString
4176      * @return {Object}
4177      */
4178     getValues : function(asString){
4179         //if (this.childForms) {
4180             // copy values from the child forms
4181         //    Roo.each(this.childForms, function (f) {
4182         //        this.setValues(f.getValues());
4183         //    }, this);
4184         //}
4185         
4186         
4187         
4188         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4189         if(asString === true){
4190             return fs;
4191         }
4192         return Roo.urlDecode(fs);
4193     },
4194     
4195     /**
4196      * Returns the fields in this form as an object with key/value pairs. 
4197      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4198      * @return {Object}
4199      */
4200     getFieldValues : function(with_hidden)
4201     {
4202         var items = this.getItems();
4203         var ret = {};
4204         items.each(function(f){
4205             if (!f.getName()) {
4206                 return;
4207             }
4208             var v = f.getValue();
4209             if (f.inputType =='radio') {
4210                 if (typeof(ret[f.getName()]) == 'undefined') {
4211                     ret[f.getName()] = ''; // empty..
4212                 }
4213                 
4214                 if (!f.el.dom.checked) {
4215                     return;
4216                     
4217                 }
4218                 v = f.el.dom.value;
4219                 
4220             }
4221             
4222             // not sure if this supported any more..
4223             if ((typeof(v) == 'object') && f.getRawValue) {
4224                 v = f.getRawValue() ; // dates..
4225             }
4226             // combo boxes where name != hiddenName...
4227             if (f.name != f.getName()) {
4228                 ret[f.name] = f.getRawValue();
4229             }
4230             ret[f.getName()] = v;
4231         });
4232         
4233         return ret;
4234     },
4235
4236     /**
4237      * Clears all invalid messages in this form.
4238      * @return {BasicForm} this
4239      */
4240     clearInvalid : function(){
4241         var items = this.getItems();
4242         
4243         items.each(function(f){
4244            f.clearInvalid();
4245         });
4246         
4247         
4248         
4249         return this;
4250     },
4251
4252     /**
4253      * Resets this form.
4254      * @return {BasicForm} this
4255      */
4256     reset : function(){
4257         var items = this.getItems();
4258         items.each(function(f){
4259             f.reset();
4260         });
4261         
4262         Roo.each(this.childForms || [], function (f) {
4263             f.reset();
4264         });
4265        
4266         
4267         return this;
4268     },
4269     getItems : function()
4270     {
4271         var r=new Roo.util.MixedCollection(false, function(o){
4272             return o.id || (o.id = Roo.id());
4273         });
4274         var iter = function(el) {
4275             if (el.inputEl) {
4276                 r.add(el);
4277             }
4278             if (!el.items) {
4279                 return;
4280             }
4281             Roo.each(el.items,function(e) {
4282                 iter(e);
4283             });
4284             
4285             
4286         };
4287         iter(this);
4288         return r;
4289         
4290         
4291         
4292         
4293     }
4294     
4295 });
4296
4297  
4298 /*
4299  * Based on:
4300  * Ext JS Library 1.1.1
4301  * Copyright(c) 2006-2007, Ext JS, LLC.
4302  *
4303  * Originally Released Under LGPL - original licence link has changed is not relivant.
4304  *
4305  * Fork - LGPL
4306  * <script type="text/javascript">
4307  */
4308 /**
4309  * @class Roo.form.VTypes
4310  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4311  * @singleton
4312  */
4313 Roo.form.VTypes = function(){
4314     // closure these in so they are only created once.
4315     var alpha = /^[a-zA-Z_]+$/;
4316     var alphanum = /^[a-zA-Z0-9_]+$/;
4317     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4318     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4319
4320     // All these messages and functions are configurable
4321     return {
4322         /**
4323          * The function used to validate email addresses
4324          * @param {String} value The email address
4325          */
4326         'email' : function(v){
4327             return email.test(v);
4328         },
4329         /**
4330          * The error text to display when the email validation function returns false
4331          * @type String
4332          */
4333         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4334         /**
4335          * The keystroke filter mask to be applied on email input
4336          * @type RegExp
4337          */
4338         'emailMask' : /[a-z0-9_\.\-@]/i,
4339
4340         /**
4341          * The function used to validate URLs
4342          * @param {String} value The URL
4343          */
4344         'url' : function(v){
4345             return url.test(v);
4346         },
4347         /**
4348          * The error text to display when the url validation function returns false
4349          * @type String
4350          */
4351         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4352         
4353         /**
4354          * The function used to validate alpha values
4355          * @param {String} value The value
4356          */
4357         'alpha' : function(v){
4358             return alpha.test(v);
4359         },
4360         /**
4361          * The error text to display when the alpha validation function returns false
4362          * @type String
4363          */
4364         'alphaText' : 'This field should only contain letters and _',
4365         /**
4366          * The keystroke filter mask to be applied on alpha input
4367          * @type RegExp
4368          */
4369         'alphaMask' : /[a-z_]/i,
4370
4371         /**
4372          * The function used to validate alphanumeric values
4373          * @param {String} value The value
4374          */
4375         'alphanum' : function(v){
4376             return alphanum.test(v);
4377         },
4378         /**
4379          * The error text to display when the alphanumeric validation function returns false
4380          * @type String
4381          */
4382         'alphanumText' : 'This field should only contain letters, numbers and _',
4383         /**
4384          * The keystroke filter mask to be applied on alphanumeric input
4385          * @type RegExp
4386          */
4387         'alphanumMask' : /[a-z0-9_]/i
4388     };
4389 }();/*
4390  * - LGPL
4391  *
4392  * Input
4393  * 
4394  */
4395
4396 /**
4397  * @class Roo.bootstrap.Input
4398  * @extends Roo.bootstrap.Component
4399  * Bootstrap Input class
4400  * @cfg {Boolean} disabled is it disabled
4401  * @cfg {String} fieldLabel - the label associated
4402  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4403  * @cfg {String} name name of the input
4404  * @cfg {string} fieldLabel - the label associated
4405  * @cfg {string}  inputType - input / file submit ...
4406  * @cfg {string} placeholder - placeholder to put in text.
4407  * @cfg {string}  before - input group add on before
4408  * @cfg {string} after - input group add on after
4409  * @cfg {string} size - (lg|sm) or leave empty..
4410  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4411  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4412  * @cfg {Number} md colspan out of 12 for computer-sized screens
4413  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4414  * @cfg {string} value default value of the input
4415  * @cfg {Number} labelWidth set the width of label (0-12)
4416  * @cfg {String} labelAlign (top|left)
4417  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4418  * 
4419  * 
4420  * @constructor
4421  * Create a new Input
4422  * @param {Object} config The config object
4423  */
4424
4425 Roo.bootstrap.Input = function(config){
4426     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4427    
4428         this.addEvents({
4429             /**
4430              * @event focus
4431              * Fires when this field receives input focus.
4432              * @param {Roo.form.Field} this
4433              */
4434             focus : true,
4435             /**
4436              * @event blur
4437              * Fires when this field loses input focus.
4438              * @param {Roo.form.Field} this
4439              */
4440             blur : true,
4441             /**
4442              * @event specialkey
4443              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4444              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4445              * @param {Roo.form.Field} this
4446              * @param {Roo.EventObject} e The event object
4447              */
4448             specialkey : true,
4449             /**
4450              * @event change
4451              * Fires just before the field blurs if the field value has changed.
4452              * @param {Roo.form.Field} this
4453              * @param {Mixed} newValue The new value
4454              * @param {Mixed} oldValue The original value
4455              */
4456             change : true,
4457             /**
4458              * @event invalid
4459              * Fires after the field has been marked as invalid.
4460              * @param {Roo.form.Field} this
4461              * @param {String} msg The validation message
4462              */
4463             invalid : true,
4464             /**
4465              * @event valid
4466              * Fires after the field has been validated with no errors.
4467              * @param {Roo.form.Field} this
4468              */
4469             valid : true,
4470              /**
4471              * @event keyup
4472              * Fires after the key up
4473              * @param {Roo.form.Field} this
4474              * @param {Roo.EventObject}  e The event Object
4475              */
4476             keyup : true
4477         });
4478 };
4479
4480 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4481      /**
4482      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4483       automatic validation (defaults to "keyup").
4484      */
4485     validationEvent : "keyup",
4486      /**
4487      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4488      */
4489     validateOnBlur : true,
4490     /**
4491      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4492      */
4493     validationDelay : 250,
4494      /**
4495      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4496      */
4497     focusClass : "x-form-focus",  // not needed???
4498     
4499        
4500     /**
4501      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4502      */
4503     invalidClass : "has-error",
4504     
4505     /**
4506      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4507      */
4508     selectOnFocus : false,
4509     
4510      /**
4511      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4512      */
4513     maskRe : null,
4514        /**
4515      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4516      */
4517     vtype : null,
4518     
4519       /**
4520      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4521      */
4522     disableKeyFilter : false,
4523     
4524        /**
4525      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4526      */
4527     disabled : false,
4528      /**
4529      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4530      */
4531     allowBlank : true,
4532     /**
4533      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4534      */
4535     blankText : "This field is required",
4536     
4537      /**
4538      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4539      */
4540     minLength : 0,
4541     /**
4542      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4543      */
4544     maxLength : Number.MAX_VALUE,
4545     /**
4546      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4547      */
4548     minLengthText : "The minimum length for this field is {0}",
4549     /**
4550      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4551      */
4552     maxLengthText : "The maximum length for this field is {0}",
4553   
4554     
4555     /**
4556      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4557      * If available, this function will be called only after the basic validators all return true, and will be passed the
4558      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4559      */
4560     validator : null,
4561     /**
4562      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4563      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4564      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4565      */
4566     regex : null,
4567     /**
4568      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4569      */
4570     regexText : "",
4571     
4572     
4573     
4574     fieldLabel : '',
4575     inputType : 'text',
4576     
4577     name : false,
4578     placeholder: false,
4579     before : false,
4580     after : false,
4581     size : false,
4582     // private
4583     hasFocus : false,
4584     preventMark: false,
4585     isFormField : true,
4586     value : '',
4587     labelWidth : 2,
4588     labelAlign : false,
4589     readOnly : false,
4590     
4591     parentLabelAlign : function()
4592     {
4593         var parent = this;
4594         while (parent.parent()) {
4595             parent = parent.parent();
4596             if (typeof(parent.labelAlign) !='undefined') {
4597                 return parent.labelAlign;
4598             }
4599         }
4600         return 'left';
4601         
4602     },
4603     
4604     getAutoCreate : function(){
4605         
4606         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4607         
4608         var id = Roo.id();
4609         
4610         var cfg = {};
4611         
4612         if(this.inputType != 'hidden'){
4613             cfg.cls = 'form-group' //input-group
4614         }
4615         
4616         var input =  {
4617             tag: 'input',
4618             id : id,
4619             type : this.inputType,
4620             value : this.value,
4621             cls : 'form-control',
4622             placeholder : this.placeholder || ''
4623             
4624         };
4625         
4626         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4627             input.maxLength = this.maxLength;
4628         }
4629         
4630         if (this.disabled) {
4631             input.disabled=true;
4632         }
4633         
4634         if (this.readOnly) {
4635             input.readonly=true;
4636         }
4637         
4638         if (this.name) {
4639             input.name = this.name;
4640         }
4641         if (this.size) {
4642             input.cls += ' input-' + this.size;
4643         }
4644         var settings=this;
4645         ['xs','sm','md','lg'].map(function(size){
4646             if (settings[size]) {
4647                 cfg.cls += ' col-' + size + '-' + settings[size];
4648             }
4649         });
4650         
4651         var inputblock = input;
4652         
4653         if (this.before || this.after) {
4654             
4655             inputblock = {
4656                 cls : 'input-group',
4657                 cn :  [] 
4658             };
4659             if (this.before) {
4660                 inputblock.cn.push({
4661                     tag :'span',
4662                     cls : 'input-group-addon',
4663                     html : this.before
4664                 });
4665             }
4666             inputblock.cn.push(input);
4667             if (this.after) {
4668                 inputblock.cn.push({
4669                     tag :'span',
4670                     cls : 'input-group-addon',
4671                     html : this.after
4672                 });
4673             }
4674             
4675         };
4676         
4677         if (align ==='left' && this.fieldLabel.length) {
4678                 Roo.log("left and has label");
4679                 cfg.cn = [
4680                     
4681                     {
4682                         tag: 'label',
4683                         'for' :  id,
4684                         cls : 'control-label col-sm-' + this.labelWidth,
4685                         html : this.fieldLabel
4686                         
4687                     },
4688                     {
4689                         cls : "col-sm-" + (12 - this.labelWidth), 
4690                         cn: [
4691                             inputblock
4692                         ]
4693                     }
4694                     
4695                 ];
4696         } else if ( this.fieldLabel.length) {
4697                 Roo.log(" label");
4698                  cfg.cn = [
4699                    
4700                     {
4701                         tag: 'label',
4702                         //cls : 'input-group-addon',
4703                         html : this.fieldLabel
4704                         
4705                     },
4706                     
4707                     inputblock
4708                     
4709                 ];
4710
4711         } else {
4712             
4713                 Roo.log(" no label && no align");
4714                 cfg.cn = [
4715                     
4716                         inputblock
4717                     
4718                 ];
4719                 
4720                 
4721         };
4722         Roo.log('input-parentType: ' + this.parentType);
4723         
4724         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4725            cfg.cls += ' navbar-form';
4726            Roo.log(cfg);
4727         }
4728         
4729         return cfg;
4730         
4731     },
4732     /**
4733      * return the real input element.
4734      */
4735     inputEl: function ()
4736     {
4737         return this.el.select('input.form-control',true).first();
4738     },
4739     setDisabled : function(v)
4740     {
4741         var i  = this.inputEl().dom;
4742         if (!v) {
4743             i.removeAttribute('disabled');
4744             return;
4745             
4746         }
4747         i.setAttribute('disabled','true');
4748     },
4749     initEvents : function()
4750     {
4751         
4752         this.inputEl().on("keydown" , this.fireKey,  this);
4753         this.inputEl().on("focus", this.onFocus,  this);
4754         this.inputEl().on("blur", this.onBlur,  this);
4755         
4756         this.inputEl().relayEvent('keyup', this);
4757
4758         // reference to original value for reset
4759         this.originalValue = this.getValue();
4760         //Roo.form.TextField.superclass.initEvents.call(this);
4761         if(this.validationEvent == 'keyup'){
4762             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4763             this.inputEl().on('keyup', this.filterValidation, this);
4764         }
4765         else if(this.validationEvent !== false){
4766             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4767         }
4768         
4769         if(this.selectOnFocus){
4770             this.on("focus", this.preFocus, this);
4771             
4772         }
4773         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4774             this.inputEl().on("keypress", this.filterKeys, this);
4775         }
4776        /* if(this.grow){
4777             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4778             this.el.on("click", this.autoSize,  this);
4779         }
4780         */
4781         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4782             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4783         }
4784         
4785     },
4786     filterValidation : function(e){
4787         if(!e.isNavKeyPress()){
4788             this.validationTask.delay(this.validationDelay);
4789         }
4790     },
4791      /**
4792      * Validates the field value
4793      * @return {Boolean} True if the value is valid, else false
4794      */
4795     validate : function(){
4796         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4797         if(this.disabled || this.validateValue(this.getRawValue())){
4798             this.clearInvalid();
4799             return true;
4800         }
4801         return false;
4802     },
4803     
4804     
4805     /**
4806      * Validates a value according to the field's validation rules and marks the field as invalid
4807      * if the validation fails
4808      * @param {Mixed} value The value to validate
4809      * @return {Boolean} True if the value is valid, else false
4810      */
4811     validateValue : function(value){
4812         if(value.length < 1)  { // if it's blank
4813              if(this.allowBlank){
4814                 this.clearInvalid();
4815                 return true;
4816              }else{
4817                 this.markInvalid(this.blankText);
4818                 return false;
4819              }
4820         }
4821         if(value.length < this.minLength){
4822             this.markInvalid(String.format(this.minLengthText, this.minLength));
4823             return false;
4824         }
4825         if(value.length > this.maxLength){
4826             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4827             return false;
4828         }
4829         if(this.vtype){
4830             var vt = Roo.form.VTypes;
4831             if(!vt[this.vtype](value, this)){
4832                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4833                 return false;
4834             }
4835         }
4836         if(typeof this.validator == "function"){
4837             var msg = this.validator(value);
4838             if(msg !== true){
4839                 this.markInvalid(msg);
4840                 return false;
4841             }
4842         }
4843         if(this.regex && !this.regex.test(value)){
4844             this.markInvalid(this.regexText);
4845             return false;
4846         }
4847         return true;
4848     },
4849
4850     
4851     
4852      // private
4853     fireKey : function(e){
4854         //Roo.log('field ' + e.getKey());
4855         if(e.isNavKeyPress()){
4856             this.fireEvent("specialkey", this, e);
4857         }
4858     },
4859     focus : function (selectText){
4860         if(this.rendered){
4861             this.inputEl().focus();
4862             if(selectText === true){
4863                 this.inputEl().dom.select();
4864             }
4865         }
4866         return this;
4867     } ,
4868     
4869     onFocus : function(){
4870         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4871            // this.el.addClass(this.focusClass);
4872         }
4873         if(!this.hasFocus){
4874             this.hasFocus = true;
4875             this.startValue = this.getValue();
4876             this.fireEvent("focus", this);
4877         }
4878     },
4879     
4880     beforeBlur : Roo.emptyFn,
4881
4882     
4883     // private
4884     onBlur : function(){
4885         this.beforeBlur();
4886         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4887             //this.el.removeClass(this.focusClass);
4888         }
4889         this.hasFocus = false;
4890         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4891             this.validate();
4892         }
4893         var v = this.getValue();
4894         if(String(v) !== String(this.startValue)){
4895             this.fireEvent('change', this, v, this.startValue);
4896         }
4897         this.fireEvent("blur", this);
4898     },
4899     
4900     /**
4901      * Resets the current field value to the originally loaded value and clears any validation messages
4902      */
4903     reset : function(){
4904         this.setValue(this.originalValue);
4905         this.clearInvalid();
4906     },
4907      /**
4908      * Returns the name of the field
4909      * @return {Mixed} name The name field
4910      */
4911     getName: function(){
4912         return this.name;
4913     },
4914      /**
4915      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4916      * @return {Mixed} value The field value
4917      */
4918     getValue : function(){
4919         return this.inputEl().getValue();
4920     },
4921     /**
4922      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4923      * @return {Mixed} value The field value
4924      */
4925     getRawValue : function(){
4926         var v = this.inputEl().getValue();
4927         
4928         return v;
4929     },
4930     
4931     /**
4932      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4933      * @param {Mixed} value The value to set
4934      */
4935     setRawValue : function(v){
4936         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4937     },
4938     
4939     selectText : function(start, end){
4940         var v = this.getRawValue();
4941         if(v.length > 0){
4942             start = start === undefined ? 0 : start;
4943             end = end === undefined ? v.length : end;
4944             var d = this.inputEl().dom;
4945             if(d.setSelectionRange){
4946                 d.setSelectionRange(start, end);
4947             }else if(d.createTextRange){
4948                 var range = d.createTextRange();
4949                 range.moveStart("character", start);
4950                 range.moveEnd("character", v.length-end);
4951                 range.select();
4952             }
4953         }
4954     },
4955     
4956     /**
4957      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4958      * @param {Mixed} value The value to set
4959      */
4960     setValue : function(v){
4961         this.value = v;
4962         if(this.rendered){
4963             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4964             this.validate();
4965         }
4966     },
4967     
4968     /*
4969     processValue : function(value){
4970         if(this.stripCharsRe){
4971             var newValue = value.replace(this.stripCharsRe, '');
4972             if(newValue !== value){
4973                 this.setRawValue(newValue);
4974                 return newValue;
4975             }
4976         }
4977         return value;
4978     },
4979   */
4980     preFocus : function(){
4981         
4982         if(this.selectOnFocus){
4983             this.inputEl().dom.select();
4984         }
4985     },
4986     filterKeys : function(e){
4987         var k = e.getKey();
4988         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4989             return;
4990         }
4991         var c = e.getCharCode(), cc = String.fromCharCode(c);
4992         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4993             return;
4994         }
4995         if(!this.maskRe.test(cc)){
4996             e.stopEvent();
4997         }
4998     },
4999      /**
5000      * Clear any invalid styles/messages for this field
5001      */
5002     clearInvalid : function(){
5003         
5004         if(!this.el || this.preventMark){ // not rendered
5005             return;
5006         }
5007         this.el.removeClass(this.invalidClass);
5008         /*
5009         switch(this.msgTarget){
5010             case 'qtip':
5011                 this.el.dom.qtip = '';
5012                 break;
5013             case 'title':
5014                 this.el.dom.title = '';
5015                 break;
5016             case 'under':
5017                 if(this.errorEl){
5018                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5019                 }
5020                 break;
5021             case 'side':
5022                 if(this.errorIcon){
5023                     this.errorIcon.dom.qtip = '';
5024                     this.errorIcon.hide();
5025                     this.un('resize', this.alignErrorIcon, this);
5026                 }
5027                 break;
5028             default:
5029                 var t = Roo.getDom(this.msgTarget);
5030                 t.innerHTML = '';
5031                 t.style.display = 'none';
5032                 break;
5033         }
5034         */
5035         this.fireEvent('valid', this);
5036     },
5037      /**
5038      * Mark this field as invalid
5039      * @param {String} msg The validation message
5040      */
5041     markInvalid : function(msg){
5042         if(!this.el  || this.preventMark){ // not rendered
5043             return;
5044         }
5045         this.el.addClass(this.invalidClass);
5046         /*
5047         msg = msg || this.invalidText;
5048         switch(this.msgTarget){
5049             case 'qtip':
5050                 this.el.dom.qtip = msg;
5051                 this.el.dom.qclass = 'x-form-invalid-tip';
5052                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5053                     Roo.QuickTips.enable();
5054                 }
5055                 break;
5056             case 'title':
5057                 this.el.dom.title = msg;
5058                 break;
5059             case 'under':
5060                 if(!this.errorEl){
5061                     var elp = this.el.findParent('.x-form-element', 5, true);
5062                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5063                     this.errorEl.setWidth(elp.getWidth(true)-20);
5064                 }
5065                 this.errorEl.update(msg);
5066                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5067                 break;
5068             case 'side':
5069                 if(!this.errorIcon){
5070                     var elp = this.el.findParent('.x-form-element', 5, true);
5071                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5072                 }
5073                 this.alignErrorIcon();
5074                 this.errorIcon.dom.qtip = msg;
5075                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5076                 this.errorIcon.show();
5077                 this.on('resize', this.alignErrorIcon, this);
5078                 break;
5079             default:
5080                 var t = Roo.getDom(this.msgTarget);
5081                 t.innerHTML = msg;
5082                 t.style.display = this.msgDisplay;
5083                 break;
5084         }
5085         */
5086         this.fireEvent('invalid', this, msg);
5087     },
5088     // private
5089     SafariOnKeyDown : function(event)
5090     {
5091         // this is a workaround for a password hang bug on chrome/ webkit.
5092         
5093         var isSelectAll = false;
5094         
5095         if(this.inputEl().dom.selectionEnd > 0){
5096             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5097         }
5098         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5099             event.preventDefault();
5100             this.setValue('');
5101             return;
5102         }
5103         
5104         if(isSelectAll){ // backspace and delete key
5105             
5106             event.preventDefault();
5107             // this is very hacky as keydown always get's upper case.
5108             //
5109             var cc = String.fromCharCode(event.getCharCode());
5110             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5111             
5112         }
5113     },
5114     adjustWidth : function(tag, w){
5115         tag = tag.toLowerCase();
5116         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5117             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5118                 if(tag == 'input'){
5119                     return w + 2;
5120                 }
5121                 if(tag == 'textarea'){
5122                     return w-2;
5123                 }
5124             }else if(Roo.isOpera){
5125                 if(tag == 'input'){
5126                     return w + 2;
5127                 }
5128                 if(tag == 'textarea'){
5129                     return w-2;
5130                 }
5131             }
5132         }
5133         return w;
5134     }
5135     
5136 });
5137
5138  
5139 /*
5140  * - LGPL
5141  *
5142  * Input
5143  * 
5144  */
5145
5146 /**
5147  * @class Roo.bootstrap.TextArea
5148  * @extends Roo.bootstrap.Input
5149  * Bootstrap TextArea class
5150  * @cfg {Number} cols Specifies the visible width of a text area
5151  * @cfg {Number} rows Specifies the visible number of lines in a text area
5152  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5153  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5154  * @cfg {string} html text
5155  * 
5156  * @constructor
5157  * Create a new TextArea
5158  * @param {Object} config The config object
5159  */
5160
5161 Roo.bootstrap.TextArea = function(config){
5162     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5163    
5164 };
5165
5166 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5167      
5168     cols : false,
5169     rows : 5,
5170     readOnly : false,
5171     warp : 'soft',
5172     resize : false,
5173     value: false,
5174     html: false,
5175     
5176     getAutoCreate : function(){
5177         
5178         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5179         
5180         var id = Roo.id();
5181         
5182         var cfg = {};
5183         
5184         var input =  {
5185             tag: 'textarea',
5186             id : id,
5187             warp : this.warp,
5188             rows : this.rows,
5189             value : this.value || '',
5190             html: this.html || '',
5191             cls : 'form-control',
5192             placeholder : this.placeholder || '' 
5193             
5194         };
5195         
5196         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5197             input.maxLength = this.maxLength;
5198         }
5199         
5200         if(this.resize){
5201             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5202         }
5203         
5204         if(this.cols){
5205             input.cols = this.cols;
5206         }
5207         
5208         if (this.readOnly) {
5209             input.readonly = true;
5210         }
5211         
5212         if (this.name) {
5213             input.name = this.name;
5214         }
5215         
5216         if (this.size) {
5217             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5218         }
5219         
5220         var settings=this;
5221         ['xs','sm','md','lg'].map(function(size){
5222             if (settings[size]) {
5223                 cfg.cls += ' col-' + size + '-' + settings[size];
5224             }
5225         });
5226         
5227         var inputblock = input;
5228         
5229         if (this.before || this.after) {
5230             
5231             inputblock = {
5232                 cls : 'input-group',
5233                 cn :  [] 
5234             };
5235             if (this.before) {
5236                 inputblock.cn.push({
5237                     tag :'span',
5238                     cls : 'input-group-addon',
5239                     html : this.before
5240                 });
5241             }
5242             inputblock.cn.push(input);
5243             if (this.after) {
5244                 inputblock.cn.push({
5245                     tag :'span',
5246                     cls : 'input-group-addon',
5247                     html : this.after
5248                 });
5249             }
5250             
5251         }
5252         
5253         if (align ==='left' && this.fieldLabel.length) {
5254                 Roo.log("left and has label");
5255                 cfg.cn = [
5256                     
5257                     {
5258                         tag: 'label',
5259                         'for' :  id,
5260                         cls : 'control-label col-sm-' + this.labelWidth,
5261                         html : this.fieldLabel
5262                         
5263                     },
5264                     {
5265                         cls : "col-sm-" + (12 - this.labelWidth), 
5266                         cn: [
5267                             inputblock
5268                         ]
5269                     }
5270                     
5271                 ];
5272         } else if ( this.fieldLabel.length) {
5273                 Roo.log(" label");
5274                  cfg.cn = [
5275                    
5276                     {
5277                         tag: 'label',
5278                         //cls : 'input-group-addon',
5279                         html : this.fieldLabel
5280                         
5281                     },
5282                     
5283                     inputblock
5284                     
5285                 ];
5286
5287         } else {
5288             
5289                    Roo.log(" no label && no align");
5290                 cfg.cn = [
5291                     
5292                         inputblock
5293                     
5294                 ];
5295                 
5296                 
5297         }
5298         
5299         if (this.disabled) {
5300             input.disabled=true;
5301         }
5302         
5303         return cfg;
5304         
5305     },
5306     /**
5307      * return the real textarea element.
5308      */
5309     inputEl: function ()
5310     {
5311         return this.el.select('textarea.form-control',true).first();
5312     }
5313 });
5314
5315  
5316 /*
5317  * - LGPL
5318  *
5319  * trigger field - base class for combo..
5320  * 
5321  */
5322  
5323 /**
5324  * @class Roo.bootstrap.TriggerField
5325  * @extends Roo.bootstrap.Input
5326  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5327  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5328  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5329  * for which you can provide a custom implementation.  For example:
5330  * <pre><code>
5331 var trigger = new Roo.bootstrap.TriggerField();
5332 trigger.onTriggerClick = myTriggerFn;
5333 trigger.applyTo('my-field');
5334 </code></pre>
5335  *
5336  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5337  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5338  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5339  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5340  * @constructor
5341  * Create a new TriggerField.
5342  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5343  * to the base TextField)
5344  */
5345 Roo.bootstrap.TriggerField = function(config){
5346     this.mimicing = false;
5347     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5348 };
5349
5350 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5351     /**
5352      * @cfg {String} triggerClass A CSS class to apply to the trigger
5353      */
5354      /**
5355      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5356      */
5357     hideTrigger:false,
5358
5359     /** @cfg {Boolean} grow @hide */
5360     /** @cfg {Number} growMin @hide */
5361     /** @cfg {Number} growMax @hide */
5362
5363     /**
5364      * @hide 
5365      * @method
5366      */
5367     autoSize: Roo.emptyFn,
5368     // private
5369     monitorTab : true,
5370     // private
5371     deferHeight : true,
5372
5373     
5374     actionMode : 'wrap',
5375     
5376     
5377     
5378     getAutoCreate : function(){
5379        
5380         var parent = this.parent();
5381         
5382         var align = this.parentLabelAlign();
5383         
5384         var id = Roo.id();
5385         
5386         var cfg = {
5387             cls: 'form-group' //input-group
5388         };
5389         
5390         
5391         var input =  {
5392             tag: 'input',
5393             id : id,
5394             type : this.inputType,
5395             cls : 'form-control',
5396             autocomplete: 'off',
5397             placeholder : this.placeholder || '' 
5398             
5399         };
5400         if (this.name) {
5401             input.name = this.name;
5402         }
5403         if (this.size) {
5404             input.cls += ' input-' + this.size;
5405         }
5406         
5407         if (this.disabled) {
5408             input.disabled=true;
5409         }
5410         
5411         var inputblock = input;
5412         
5413         if (this.before || this.after) {
5414             
5415             inputblock = {
5416                 cls : 'input-group',
5417                 cn :  [] 
5418             };
5419             if (this.before) {
5420                 inputblock.cn.push({
5421                     tag :'span',
5422                     cls : 'input-group-addon',
5423                     html : this.before
5424                 });
5425             }
5426             inputblock.cn.push(input);
5427             if (this.after) {
5428                 inputblock.cn.push({
5429                     tag :'span',
5430                     cls : 'input-group-addon',
5431                     html : this.after
5432                 });
5433             }
5434             
5435         };
5436         
5437         var box = {
5438             tag: 'div',
5439             cn: [
5440                 {
5441                     tag: 'input',
5442                     type : 'hidden',
5443                     cls: 'form-hidden-field'
5444                 },
5445                 inputblock
5446             ]
5447             
5448         };
5449         
5450         if(this.multiple){
5451             Roo.log('multiple');
5452             
5453             box = {
5454                 tag: 'div',
5455                 cn: [
5456                     {
5457                         tag: 'input',
5458                         type : 'hidden',
5459                         cls: 'form-hidden-field'
5460                     },
5461                     {
5462                         tag: 'ul',
5463                         cls: 'select2-choices',
5464                         cn:[
5465                             {
5466                                 tag: 'li',
5467                                 cls: 'select2-search-field',
5468                                 cn: [
5469
5470                                     inputblock
5471                                 ]
5472                             }
5473                         ]
5474                     }
5475                 ]
5476             }
5477         };
5478         
5479         var combobox = {
5480             cls: 'select2-container input-group',
5481             cn: [
5482                 box,
5483                 {
5484                     tag: 'ul',
5485                     cls: 'typeahead typeahead-long dropdown-menu',
5486                     style: 'display:none'
5487                 }
5488             ]
5489         };
5490         
5491         if(!this.multiple){
5492             combobox.cn.push({
5493                 tag :'span',
5494                 cls : 'input-group-addon btn dropdown-toggle',
5495                 cn : [
5496                     {
5497                         tag: 'span',
5498                         cls: 'caret'
5499                     },
5500                     {
5501                         tag: 'span',
5502                         cls: 'combobox-clear',
5503                         cn  : [
5504                             {
5505                                 tag : 'i',
5506                                 cls: 'icon-remove'
5507                             }
5508                         ]
5509                     }
5510                 ]
5511
5512             })
5513         }
5514         
5515         if(this.multiple){
5516             combobox.cls += ' select2-container-multi';
5517         }
5518         
5519         if (align ==='left' && this.fieldLabel.length) {
5520             
5521                 Roo.log("left and has label");
5522                 cfg.cn = [
5523                     
5524                     {
5525                         tag: 'label',
5526                         'for' :  id,
5527                         cls : 'control-label col-sm-' + this.labelWidth,
5528                         html : this.fieldLabel
5529                         
5530                     },
5531                     {
5532                         cls : "col-sm-" + (12 - this.labelWidth), 
5533                         cn: [
5534                             combobox
5535                         ]
5536                     }
5537                     
5538                 ];
5539         } else if ( this.fieldLabel.length) {
5540                 Roo.log(" label");
5541                  cfg.cn = [
5542                    
5543                     {
5544                         tag: 'label',
5545                         //cls : 'input-group-addon',
5546                         html : this.fieldLabel
5547                         
5548                     },
5549                     
5550                     combobox
5551                     
5552                 ];
5553
5554         } else {
5555             
5556                 Roo.log(" no label && no align");
5557                 cfg = combobox
5558                      
5559                 
5560         }
5561          
5562         var settings=this;
5563         ['xs','sm','md','lg'].map(function(size){
5564             if (settings[size]) {
5565                 cfg.cls += ' col-' + size + '-' + settings[size];
5566             }
5567         });
5568         
5569         return cfg;
5570         
5571     },
5572     
5573     
5574     
5575     // private
5576     onResize : function(w, h){
5577 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5578 //        if(typeof w == 'number'){
5579 //            var x = w - this.trigger.getWidth();
5580 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5581 //            this.trigger.setStyle('left', x+'px');
5582 //        }
5583     },
5584
5585     // private
5586     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5587
5588     // private
5589     getResizeEl : function(){
5590         return this.inputEl();
5591     },
5592
5593     // private
5594     getPositionEl : function(){
5595         return this.inputEl();
5596     },
5597
5598     // private
5599     alignErrorIcon : function(){
5600         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5601     },
5602
5603     // private
5604     initEvents : function(){
5605         
5606         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5607         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5608         if(!this.multiple){
5609             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5610             if(this.hideTrigger){
5611                 this.trigger.setDisplayed(false);
5612             }
5613             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5614         }
5615         
5616         if(this.multiple){
5617             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5618         }
5619         
5620         //this.trigger.addClassOnOver('x-form-trigger-over');
5621         //this.trigger.addClassOnClick('x-form-trigger-click');
5622         
5623         //if(!this.width){
5624         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5625         //}
5626     },
5627
5628     // private
5629     initTrigger : function(){
5630        
5631     },
5632
5633     // private
5634     onDestroy : function(){
5635         if(this.trigger){
5636             this.trigger.removeAllListeners();
5637           //  this.trigger.remove();
5638         }
5639         //if(this.wrap){
5640         //    this.wrap.remove();
5641         //}
5642         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5643     },
5644
5645     // private
5646     onFocus : function(){
5647         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5648         /*
5649         if(!this.mimicing){
5650             this.wrap.addClass('x-trigger-wrap-focus');
5651             this.mimicing = true;
5652             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5653             if(this.monitorTab){
5654                 this.el.on("keydown", this.checkTab, this);
5655             }
5656         }
5657         */
5658     },
5659
5660     // private
5661     checkTab : function(e){
5662         if(e.getKey() == e.TAB){
5663             this.triggerBlur();
5664         }
5665     },
5666
5667     // private
5668     onBlur : function(){
5669         // do nothing
5670     },
5671
5672     // private
5673     mimicBlur : function(e, t){
5674         /*
5675         if(!this.wrap.contains(t) && this.validateBlur()){
5676             this.triggerBlur();
5677         }
5678         */
5679     },
5680
5681     // private
5682     triggerBlur : function(){
5683         this.mimicing = false;
5684         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5685         if(this.monitorTab){
5686             this.el.un("keydown", this.checkTab, this);
5687         }
5688         //this.wrap.removeClass('x-trigger-wrap-focus');
5689         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5690     },
5691
5692     // private
5693     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5694     validateBlur : function(e, t){
5695         return true;
5696     },
5697
5698     // private
5699     onDisable : function(){
5700         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5701         //if(this.wrap){
5702         //    this.wrap.addClass('x-item-disabled');
5703         //}
5704     },
5705
5706     // private
5707     onEnable : function(){
5708         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5709         //if(this.wrap){
5710         //    this.el.removeClass('x-item-disabled');
5711         //}
5712     },
5713
5714     // private
5715     onShow : function(){
5716         var ae = this.getActionEl();
5717         
5718         if(ae){
5719             ae.dom.style.display = '';
5720             ae.dom.style.visibility = 'visible';
5721         }
5722     },
5723
5724     // private
5725     
5726     onHide : function(){
5727         var ae = this.getActionEl();
5728         ae.dom.style.display = 'none';
5729     },
5730
5731     /**
5732      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5733      * by an implementing function.
5734      * @method
5735      * @param {EventObject} e
5736      */
5737     onTriggerClick : Roo.emptyFn
5738 });
5739  /*
5740  * Based on:
5741  * Ext JS Library 1.1.1
5742  * Copyright(c) 2006-2007, Ext JS, LLC.
5743  *
5744  * Originally Released Under LGPL - original licence link has changed is not relivant.
5745  *
5746  * Fork - LGPL
5747  * <script type="text/javascript">
5748  */
5749
5750
5751 /**
5752  * @class Roo.data.SortTypes
5753  * @singleton
5754  * Defines the default sorting (casting?) comparison functions used when sorting data.
5755  */
5756 Roo.data.SortTypes = {
5757     /**
5758      * Default sort that does nothing
5759      * @param {Mixed} s The value being converted
5760      * @return {Mixed} The comparison value
5761      */
5762     none : function(s){
5763         return s;
5764     },
5765     
5766     /**
5767      * The regular expression used to strip tags
5768      * @type {RegExp}
5769      * @property
5770      */
5771     stripTagsRE : /<\/?[^>]+>/gi,
5772     
5773     /**
5774      * Strips all HTML tags to sort on text only
5775      * @param {Mixed} s The value being converted
5776      * @return {String} The comparison value
5777      */
5778     asText : function(s){
5779         return String(s).replace(this.stripTagsRE, "");
5780     },
5781     
5782     /**
5783      * Strips all HTML tags to sort on text only - Case insensitive
5784      * @param {Mixed} s The value being converted
5785      * @return {String} The comparison value
5786      */
5787     asUCText : function(s){
5788         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5789     },
5790     
5791     /**
5792      * Case insensitive string
5793      * @param {Mixed} s The value being converted
5794      * @return {String} The comparison value
5795      */
5796     asUCString : function(s) {
5797         return String(s).toUpperCase();
5798     },
5799     
5800     /**
5801      * Date sorting
5802      * @param {Mixed} s The value being converted
5803      * @return {Number} The comparison value
5804      */
5805     asDate : function(s) {
5806         if(!s){
5807             return 0;
5808         }
5809         if(s instanceof Date){
5810             return s.getTime();
5811         }
5812         return Date.parse(String(s));
5813     },
5814     
5815     /**
5816      * Float sorting
5817      * @param {Mixed} s The value being converted
5818      * @return {Float} The comparison value
5819      */
5820     asFloat : function(s) {
5821         var val = parseFloat(String(s).replace(/,/g, ""));
5822         if(isNaN(val)) val = 0;
5823         return val;
5824     },
5825     
5826     /**
5827      * Integer sorting
5828      * @param {Mixed} s The value being converted
5829      * @return {Number} The comparison value
5830      */
5831     asInt : function(s) {
5832         var val = parseInt(String(s).replace(/,/g, ""));
5833         if(isNaN(val)) val = 0;
5834         return val;
5835     }
5836 };/*
5837  * Based on:
5838  * Ext JS Library 1.1.1
5839  * Copyright(c) 2006-2007, Ext JS, LLC.
5840  *
5841  * Originally Released Under LGPL - original licence link has changed is not relivant.
5842  *
5843  * Fork - LGPL
5844  * <script type="text/javascript">
5845  */
5846
5847 /**
5848 * @class Roo.data.Record
5849  * Instances of this class encapsulate both record <em>definition</em> information, and record
5850  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5851  * to access Records cached in an {@link Roo.data.Store} object.<br>
5852  * <p>
5853  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5854  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5855  * objects.<br>
5856  * <p>
5857  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5858  * @constructor
5859  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5860  * {@link #create}. The parameters are the same.
5861  * @param {Array} data An associative Array of data values keyed by the field name.
5862  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5863  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5864  * not specified an integer id is generated.
5865  */
5866 Roo.data.Record = function(data, id){
5867     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5868     this.data = data;
5869 };
5870
5871 /**
5872  * Generate a constructor for a specific record layout.
5873  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5874  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5875  * Each field definition object may contain the following properties: <ul>
5876  * <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,
5877  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5878  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5879  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5880  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5881  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5882  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5883  * this may be omitted.</p></li>
5884  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5885  * <ul><li>auto (Default, implies no conversion)</li>
5886  * <li>string</li>
5887  * <li>int</li>
5888  * <li>float</li>
5889  * <li>boolean</li>
5890  * <li>date</li></ul></p></li>
5891  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5892  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5893  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5894  * by the Reader into an object that will be stored in the Record. It is passed the
5895  * following parameters:<ul>
5896  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5897  * </ul></p></li>
5898  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5899  * </ul>
5900  * <br>usage:<br><pre><code>
5901 var TopicRecord = Roo.data.Record.create(
5902     {name: 'title', mapping: 'topic_title'},
5903     {name: 'author', mapping: 'username'},
5904     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5905     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5906     {name: 'lastPoster', mapping: 'user2'},
5907     {name: 'excerpt', mapping: 'post_text'}
5908 );
5909
5910 var myNewRecord = new TopicRecord({
5911     title: 'Do my job please',
5912     author: 'noobie',
5913     totalPosts: 1,
5914     lastPost: new Date(),
5915     lastPoster: 'Animal',
5916     excerpt: 'No way dude!'
5917 });
5918 myStore.add(myNewRecord);
5919 </code></pre>
5920  * @method create
5921  * @static
5922  */
5923 Roo.data.Record.create = function(o){
5924     var f = function(){
5925         f.superclass.constructor.apply(this, arguments);
5926     };
5927     Roo.extend(f, Roo.data.Record);
5928     var p = f.prototype;
5929     p.fields = new Roo.util.MixedCollection(false, function(field){
5930         return field.name;
5931     });
5932     for(var i = 0, len = o.length; i < len; i++){
5933         p.fields.add(new Roo.data.Field(o[i]));
5934     }
5935     f.getField = function(name){
5936         return p.fields.get(name);  
5937     };
5938     return f;
5939 };
5940
5941 Roo.data.Record.AUTO_ID = 1000;
5942 Roo.data.Record.EDIT = 'edit';
5943 Roo.data.Record.REJECT = 'reject';
5944 Roo.data.Record.COMMIT = 'commit';
5945
5946 Roo.data.Record.prototype = {
5947     /**
5948      * Readonly flag - true if this record has been modified.
5949      * @type Boolean
5950      */
5951     dirty : false,
5952     editing : false,
5953     error: null,
5954     modified: null,
5955
5956     // private
5957     join : function(store){
5958         this.store = store;
5959     },
5960
5961     /**
5962      * Set the named field to the specified value.
5963      * @param {String} name The name of the field to set.
5964      * @param {Object} value The value to set the field to.
5965      */
5966     set : function(name, value){
5967         if(this.data[name] == value){
5968             return;
5969         }
5970         this.dirty = true;
5971         if(!this.modified){
5972             this.modified = {};
5973         }
5974         if(typeof this.modified[name] == 'undefined'){
5975             this.modified[name] = this.data[name];
5976         }
5977         this.data[name] = value;
5978         if(!this.editing && this.store){
5979             this.store.afterEdit(this);
5980         }       
5981     },
5982
5983     /**
5984      * Get the value of the named field.
5985      * @param {String} name The name of the field to get the value of.
5986      * @return {Object} The value of the field.
5987      */
5988     get : function(name){
5989         return this.data[name]; 
5990     },
5991
5992     // private
5993     beginEdit : function(){
5994         this.editing = true;
5995         this.modified = {}; 
5996     },
5997
5998     // private
5999     cancelEdit : function(){
6000         this.editing = false;
6001         delete this.modified;
6002     },
6003
6004     // private
6005     endEdit : function(){
6006         this.editing = false;
6007         if(this.dirty && this.store){
6008             this.store.afterEdit(this);
6009         }
6010     },
6011
6012     /**
6013      * Usually called by the {@link Roo.data.Store} which owns the Record.
6014      * Rejects all changes made to the Record since either creation, or the last commit operation.
6015      * Modified fields are reverted to their original values.
6016      * <p>
6017      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6018      * of reject operations.
6019      */
6020     reject : function(){
6021         var m = this.modified;
6022         for(var n in m){
6023             if(typeof m[n] != "function"){
6024                 this.data[n] = m[n];
6025             }
6026         }
6027         this.dirty = false;
6028         delete this.modified;
6029         this.editing = false;
6030         if(this.store){
6031             this.store.afterReject(this);
6032         }
6033     },
6034
6035     /**
6036      * Usually called by the {@link Roo.data.Store} which owns the Record.
6037      * Commits all changes made to the Record since either creation, or the last commit operation.
6038      * <p>
6039      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6040      * of commit operations.
6041      */
6042     commit : function(){
6043         this.dirty = false;
6044         delete this.modified;
6045         this.editing = false;
6046         if(this.store){
6047             this.store.afterCommit(this);
6048         }
6049     },
6050
6051     // private
6052     hasError : function(){
6053         return this.error != null;
6054     },
6055
6056     // private
6057     clearError : function(){
6058         this.error = null;
6059     },
6060
6061     /**
6062      * Creates a copy of this record.
6063      * @param {String} id (optional) A new record id if you don't want to use this record's id
6064      * @return {Record}
6065      */
6066     copy : function(newId) {
6067         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6068     }
6069 };/*
6070  * Based on:
6071  * Ext JS Library 1.1.1
6072  * Copyright(c) 2006-2007, Ext JS, LLC.
6073  *
6074  * Originally Released Under LGPL - original licence link has changed is not relivant.
6075  *
6076  * Fork - LGPL
6077  * <script type="text/javascript">
6078  */
6079
6080
6081
6082 /**
6083  * @class Roo.data.Store
6084  * @extends Roo.util.Observable
6085  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6086  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6087  * <p>
6088  * 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
6089  * has no knowledge of the format of the data returned by the Proxy.<br>
6090  * <p>
6091  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6092  * instances from the data object. These records are cached and made available through accessor functions.
6093  * @constructor
6094  * Creates a new Store.
6095  * @param {Object} config A config object containing the objects needed for the Store to access data,
6096  * and read the data into Records.
6097  */
6098 Roo.data.Store = function(config){
6099     this.data = new Roo.util.MixedCollection(false);
6100     this.data.getKey = function(o){
6101         return o.id;
6102     };
6103     this.baseParams = {};
6104     // private
6105     this.paramNames = {
6106         "start" : "start",
6107         "limit" : "limit",
6108         "sort" : "sort",
6109         "dir" : "dir",
6110         "multisort" : "_multisort"
6111     };
6112
6113     if(config && config.data){
6114         this.inlineData = config.data;
6115         delete config.data;
6116     }
6117
6118     Roo.apply(this, config);
6119     
6120     if(this.reader){ // reader passed
6121         this.reader = Roo.factory(this.reader, Roo.data);
6122         this.reader.xmodule = this.xmodule || false;
6123         if(!this.recordType){
6124             this.recordType = this.reader.recordType;
6125         }
6126         if(this.reader.onMetaChange){
6127             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6128         }
6129     }
6130
6131     if(this.recordType){
6132         this.fields = this.recordType.prototype.fields;
6133     }
6134     this.modified = [];
6135
6136     this.addEvents({
6137         /**
6138          * @event datachanged
6139          * Fires when the data cache has changed, and a widget which is using this Store
6140          * as a Record cache should refresh its view.
6141          * @param {Store} this
6142          */
6143         datachanged : true,
6144         /**
6145          * @event metachange
6146          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6147          * @param {Store} this
6148          * @param {Object} meta The JSON metadata
6149          */
6150         metachange : true,
6151         /**
6152          * @event add
6153          * Fires when Records have been added to the Store
6154          * @param {Store} this
6155          * @param {Roo.data.Record[]} records The array of Records added
6156          * @param {Number} index The index at which the record(s) were added
6157          */
6158         add : true,
6159         /**
6160          * @event remove
6161          * Fires when a Record has been removed from the Store
6162          * @param {Store} this
6163          * @param {Roo.data.Record} record The Record that was removed
6164          * @param {Number} index The index at which the record was removed
6165          */
6166         remove : true,
6167         /**
6168          * @event update
6169          * Fires when a Record has been updated
6170          * @param {Store} this
6171          * @param {Roo.data.Record} record The Record that was updated
6172          * @param {String} operation The update operation being performed.  Value may be one of:
6173          * <pre><code>
6174  Roo.data.Record.EDIT
6175  Roo.data.Record.REJECT
6176  Roo.data.Record.COMMIT
6177          * </code></pre>
6178          */
6179         update : true,
6180         /**
6181          * @event clear
6182          * Fires when the data cache has been cleared.
6183          * @param {Store} this
6184          */
6185         clear : true,
6186         /**
6187          * @event beforeload
6188          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6189          * the load action will be canceled.
6190          * @param {Store} this
6191          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6192          */
6193         beforeload : true,
6194         /**
6195          * @event beforeloadadd
6196          * Fires after a new set of Records has been loaded.
6197          * @param {Store} this
6198          * @param {Roo.data.Record[]} records The Records that were loaded
6199          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6200          */
6201         beforeloadadd : true,
6202         /**
6203          * @event load
6204          * Fires after a new set of Records has been loaded, before they are added to the store.
6205          * @param {Store} this
6206          * @param {Roo.data.Record[]} records The Records that were loaded
6207          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6208          * @params {Object} return from reader
6209          */
6210         load : true,
6211         /**
6212          * @event loadexception
6213          * Fires if an exception occurs in the Proxy during loading.
6214          * Called with the signature of the Proxy's "loadexception" event.
6215          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6216          * 
6217          * @param {Proxy} 
6218          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6219          * @param {Object} load options 
6220          * @param {Object} jsonData from your request (normally this contains the Exception)
6221          */
6222         loadexception : true
6223     });
6224     
6225     if(this.proxy){
6226         this.proxy = Roo.factory(this.proxy, Roo.data);
6227         this.proxy.xmodule = this.xmodule || false;
6228         this.relayEvents(this.proxy,  ["loadexception"]);
6229     }
6230     this.sortToggle = {};
6231     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6232
6233     Roo.data.Store.superclass.constructor.call(this);
6234
6235     if(this.inlineData){
6236         this.loadData(this.inlineData);
6237         delete this.inlineData;
6238     }
6239 };
6240
6241 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6242      /**
6243     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6244     * without a remote query - used by combo/forms at present.
6245     */
6246     
6247     /**
6248     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6249     */
6250     /**
6251     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6252     */
6253     /**
6254     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6255     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6256     */
6257     /**
6258     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6259     * on any HTTP request
6260     */
6261     /**
6262     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6263     */
6264     /**
6265     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6266     */
6267     multiSort: false,
6268     /**
6269     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6270     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6271     */
6272     remoteSort : false,
6273
6274     /**
6275     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6276      * loaded or when a record is removed. (defaults to false).
6277     */
6278     pruneModifiedRecords : false,
6279
6280     // private
6281     lastOptions : null,
6282
6283     /**
6284      * Add Records to the Store and fires the add event.
6285      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6286      */
6287     add : function(records){
6288         records = [].concat(records);
6289         for(var i = 0, len = records.length; i < len; i++){
6290             records[i].join(this);
6291         }
6292         var index = this.data.length;
6293         this.data.addAll(records);
6294         this.fireEvent("add", this, records, index);
6295     },
6296
6297     /**
6298      * Remove a Record from the Store and fires the remove event.
6299      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6300      */
6301     remove : function(record){
6302         var index = this.data.indexOf(record);
6303         this.data.removeAt(index);
6304         if(this.pruneModifiedRecords){
6305             this.modified.remove(record);
6306         }
6307         this.fireEvent("remove", this, record, index);
6308     },
6309
6310     /**
6311      * Remove all Records from the Store and fires the clear event.
6312      */
6313     removeAll : function(){
6314         this.data.clear();
6315         if(this.pruneModifiedRecords){
6316             this.modified = [];
6317         }
6318         this.fireEvent("clear", this);
6319     },
6320
6321     /**
6322      * Inserts Records to the Store at the given index and fires the add event.
6323      * @param {Number} index The start index at which to insert the passed Records.
6324      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6325      */
6326     insert : function(index, records){
6327         records = [].concat(records);
6328         for(var i = 0, len = records.length; i < len; i++){
6329             this.data.insert(index, records[i]);
6330             records[i].join(this);
6331         }
6332         this.fireEvent("add", this, records, index);
6333     },
6334
6335     /**
6336      * Get the index within the cache of the passed Record.
6337      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6338      * @return {Number} The index of the passed Record. Returns -1 if not found.
6339      */
6340     indexOf : function(record){
6341         return this.data.indexOf(record);
6342     },
6343
6344     /**
6345      * Get the index within the cache of the Record with the passed id.
6346      * @param {String} id The id of the Record to find.
6347      * @return {Number} The index of the Record. Returns -1 if not found.
6348      */
6349     indexOfId : function(id){
6350         return this.data.indexOfKey(id);
6351     },
6352
6353     /**
6354      * Get the Record with the specified id.
6355      * @param {String} id The id of the Record to find.
6356      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6357      */
6358     getById : function(id){
6359         return this.data.key(id);
6360     },
6361
6362     /**
6363      * Get the Record at the specified index.
6364      * @param {Number} index The index of the Record to find.
6365      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6366      */
6367     getAt : function(index){
6368         return this.data.itemAt(index);
6369     },
6370
6371     /**
6372      * Returns a range of Records between specified indices.
6373      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6374      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6375      * @return {Roo.data.Record[]} An array of Records
6376      */
6377     getRange : function(start, end){
6378         return this.data.getRange(start, end);
6379     },
6380
6381     // private
6382     storeOptions : function(o){
6383         o = Roo.apply({}, o);
6384         delete o.callback;
6385         delete o.scope;
6386         this.lastOptions = o;
6387     },
6388
6389     /**
6390      * Loads the Record cache from the configured Proxy using the configured Reader.
6391      * <p>
6392      * If using remote paging, then the first load call must specify the <em>start</em>
6393      * and <em>limit</em> properties in the options.params property to establish the initial
6394      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6395      * <p>
6396      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6397      * and this call will return before the new data has been loaded. Perform any post-processing
6398      * in a callback function, or in a "load" event handler.</strong>
6399      * <p>
6400      * @param {Object} options An object containing properties which control loading options:<ul>
6401      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6402      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6403      * passed the following arguments:<ul>
6404      * <li>r : Roo.data.Record[]</li>
6405      * <li>options: Options object from the load call</li>
6406      * <li>success: Boolean success indicator</li></ul></li>
6407      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6408      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6409      * </ul>
6410      */
6411     load : function(options){
6412         options = options || {};
6413         if(this.fireEvent("beforeload", this, options) !== false){
6414             this.storeOptions(options);
6415             var p = Roo.apply(options.params || {}, this.baseParams);
6416             // if meta was not loaded from remote source.. try requesting it.
6417             if (!this.reader.metaFromRemote) {
6418                 p._requestMeta = 1;
6419             }
6420             if(this.sortInfo && this.remoteSort){
6421                 var pn = this.paramNames;
6422                 p[pn["sort"]] = this.sortInfo.field;
6423                 p[pn["dir"]] = this.sortInfo.direction;
6424             }
6425             if (this.multiSort) {
6426                 var pn = this.paramNames;
6427                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6428             }
6429             
6430             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6431         }
6432     },
6433
6434     /**
6435      * Reloads the Record cache from the configured Proxy using the configured Reader and
6436      * the options from the last load operation performed.
6437      * @param {Object} options (optional) An object containing properties which may override the options
6438      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6439      * the most recently used options are reused).
6440      */
6441     reload : function(options){
6442         this.load(Roo.applyIf(options||{}, this.lastOptions));
6443     },
6444
6445     // private
6446     // Called as a callback by the Reader during a load operation.
6447     loadRecords : function(o, options, success){
6448         if(!o || success === false){
6449             if(success !== false){
6450                 this.fireEvent("load", this, [], options, o);
6451             }
6452             if(options.callback){
6453                 options.callback.call(options.scope || this, [], options, false);
6454             }
6455             return;
6456         }
6457         // if data returned failure - throw an exception.
6458         if (o.success === false) {
6459             // show a message if no listener is registered.
6460             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6461                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6462             }
6463             // loadmask wil be hooked into this..
6464             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6465             return;
6466         }
6467         var r = o.records, t = o.totalRecords || r.length;
6468         
6469         this.fireEvent("beforeloadadd", this, r, options, o);
6470         
6471         if(!options || options.add !== true){
6472             if(this.pruneModifiedRecords){
6473                 this.modified = [];
6474             }
6475             for(var i = 0, len = r.length; i < len; i++){
6476                 r[i].join(this);
6477             }
6478             if(this.snapshot){
6479                 this.data = this.snapshot;
6480                 delete this.snapshot;
6481             }
6482             this.data.clear();
6483             this.data.addAll(r);
6484             this.totalLength = t;
6485             this.applySort();
6486             this.fireEvent("datachanged", this);
6487         }else{
6488             this.totalLength = Math.max(t, this.data.length+r.length);
6489             this.add(r);
6490         }
6491         this.fireEvent("load", this, r, options, o);
6492         if(options.callback){
6493             options.callback.call(options.scope || this, r, options, true);
6494         }
6495     },
6496
6497
6498     /**
6499      * Loads data from a passed data block. A Reader which understands the format of the data
6500      * must have been configured in the constructor.
6501      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6502      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6503      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6504      */
6505     loadData : function(o, append){
6506         var r = this.reader.readRecords(o);
6507         this.loadRecords(r, {add: append}, true);
6508     },
6509
6510     /**
6511      * Gets the number of cached records.
6512      * <p>
6513      * <em>If using paging, this may not be the total size of the dataset. If the data object
6514      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6515      * the data set size</em>
6516      */
6517     getCount : function(){
6518         return this.data.length || 0;
6519     },
6520
6521     /**
6522      * Gets the total number of records in the dataset as returned by the server.
6523      * <p>
6524      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6525      * the dataset size</em>
6526      */
6527     getTotalCount : function(){
6528         return this.totalLength || 0;
6529     },
6530
6531     /**
6532      * Returns the sort state of the Store as an object with two properties:
6533      * <pre><code>
6534  field {String} The name of the field by which the Records are sorted
6535  direction {String} The sort order, "ASC" or "DESC"
6536      * </code></pre>
6537      */
6538     getSortState : function(){
6539         return this.sortInfo;
6540     },
6541
6542     // private
6543     applySort : function(){
6544         if(this.sortInfo && !this.remoteSort){
6545             var s = this.sortInfo, f = s.field;
6546             var st = this.fields.get(f).sortType;
6547             var fn = function(r1, r2){
6548                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6549                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6550             };
6551             this.data.sort(s.direction, fn);
6552             if(this.snapshot && this.snapshot != this.data){
6553                 this.snapshot.sort(s.direction, fn);
6554             }
6555         }
6556     },
6557
6558     /**
6559      * Sets the default sort column and order to be used by the next load operation.
6560      * @param {String} fieldName The name of the field to sort by.
6561      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6562      */
6563     setDefaultSort : function(field, dir){
6564         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6565     },
6566
6567     /**
6568      * Sort the Records.
6569      * If remote sorting is used, the sort is performed on the server, and the cache is
6570      * reloaded. If local sorting is used, the cache is sorted internally.
6571      * @param {String} fieldName The name of the field to sort by.
6572      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6573      */
6574     sort : function(fieldName, dir){
6575         var f = this.fields.get(fieldName);
6576         if(!dir){
6577             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6578             
6579             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6580                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6581             }else{
6582                 dir = f.sortDir;
6583             }
6584         }
6585         this.sortToggle[f.name] = dir;
6586         this.sortInfo = {field: f.name, direction: dir};
6587         if(!this.remoteSort){
6588             this.applySort();
6589             this.fireEvent("datachanged", this);
6590         }else{
6591             this.load(this.lastOptions);
6592         }
6593     },
6594
6595     /**
6596      * Calls the specified function for each of the Records in the cache.
6597      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6598      * Returning <em>false</em> aborts and exits the iteration.
6599      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6600      */
6601     each : function(fn, scope){
6602         this.data.each(fn, scope);
6603     },
6604
6605     /**
6606      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6607      * (e.g., during paging).
6608      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6609      */
6610     getModifiedRecords : function(){
6611         return this.modified;
6612     },
6613
6614     // private
6615     createFilterFn : function(property, value, anyMatch){
6616         if(!value.exec){ // not a regex
6617             value = String(value);
6618             if(value.length == 0){
6619                 return false;
6620             }
6621             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6622         }
6623         return function(r){
6624             return value.test(r.data[property]);
6625         };
6626     },
6627
6628     /**
6629      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6630      * @param {String} property A field on your records
6631      * @param {Number} start The record index to start at (defaults to 0)
6632      * @param {Number} end The last record index to include (defaults to length - 1)
6633      * @return {Number} The sum
6634      */
6635     sum : function(property, start, end){
6636         var rs = this.data.items, v = 0;
6637         start = start || 0;
6638         end = (end || end === 0) ? end : rs.length-1;
6639
6640         for(var i = start; i <= end; i++){
6641             v += (rs[i].data[property] || 0);
6642         }
6643         return v;
6644     },
6645
6646     /**
6647      * Filter the records by a specified property.
6648      * @param {String} field A field on your records
6649      * @param {String/RegExp} value Either a string that the field
6650      * should start with or a RegExp to test against the field
6651      * @param {Boolean} anyMatch True to match any part not just the beginning
6652      */
6653     filter : function(property, value, anyMatch){
6654         var fn = this.createFilterFn(property, value, anyMatch);
6655         return fn ? this.filterBy(fn) : this.clearFilter();
6656     },
6657
6658     /**
6659      * Filter by a function. The specified function will be called with each
6660      * record in this data source. If the function returns true the record is included,
6661      * otherwise it is filtered.
6662      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6663      * @param {Object} scope (optional) The scope of the function (defaults to this)
6664      */
6665     filterBy : function(fn, scope){
6666         this.snapshot = this.snapshot || this.data;
6667         this.data = this.queryBy(fn, scope||this);
6668         this.fireEvent("datachanged", this);
6669     },
6670
6671     /**
6672      * Query the records by a specified property.
6673      * @param {String} field A field on your records
6674      * @param {String/RegExp} value Either a string that the field
6675      * should start with or a RegExp to test against the field
6676      * @param {Boolean} anyMatch True to match any part not just the beginning
6677      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6678      */
6679     query : function(property, value, anyMatch){
6680         var fn = this.createFilterFn(property, value, anyMatch);
6681         return fn ? this.queryBy(fn) : this.data.clone();
6682     },
6683
6684     /**
6685      * Query by a function. The specified function will be called with each
6686      * record in this data source. If the function returns true the record is included
6687      * in the results.
6688      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6689      * @param {Object} scope (optional) The scope of the function (defaults to this)
6690       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6691      **/
6692     queryBy : function(fn, scope){
6693         var data = this.snapshot || this.data;
6694         return data.filterBy(fn, scope||this);
6695     },
6696
6697     /**
6698      * Collects unique values for a particular dataIndex from this store.
6699      * @param {String} dataIndex The property to collect
6700      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6701      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6702      * @return {Array} An array of the unique values
6703      **/
6704     collect : function(dataIndex, allowNull, bypassFilter){
6705         var d = (bypassFilter === true && this.snapshot) ?
6706                 this.snapshot.items : this.data.items;
6707         var v, sv, r = [], l = {};
6708         for(var i = 0, len = d.length; i < len; i++){
6709             v = d[i].data[dataIndex];
6710             sv = String(v);
6711             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6712                 l[sv] = true;
6713                 r[r.length] = v;
6714             }
6715         }
6716         return r;
6717     },
6718
6719     /**
6720      * Revert to a view of the Record cache with no filtering applied.
6721      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6722      */
6723     clearFilter : function(suppressEvent){
6724         if(this.snapshot && this.snapshot != this.data){
6725             this.data = this.snapshot;
6726             delete this.snapshot;
6727             if(suppressEvent !== true){
6728                 this.fireEvent("datachanged", this);
6729             }
6730         }
6731     },
6732
6733     // private
6734     afterEdit : function(record){
6735         if(this.modified.indexOf(record) == -1){
6736             this.modified.push(record);
6737         }
6738         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6739     },
6740     
6741     // private
6742     afterReject : function(record){
6743         this.modified.remove(record);
6744         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6745     },
6746
6747     // private
6748     afterCommit : function(record){
6749         this.modified.remove(record);
6750         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6751     },
6752
6753     /**
6754      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6755      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6756      */
6757     commitChanges : function(){
6758         var m = this.modified.slice(0);
6759         this.modified = [];
6760         for(var i = 0, len = m.length; i < len; i++){
6761             m[i].commit();
6762         }
6763     },
6764
6765     /**
6766      * Cancel outstanding changes on all changed records.
6767      */
6768     rejectChanges : function(){
6769         var m = this.modified.slice(0);
6770         this.modified = [];
6771         for(var i = 0, len = m.length; i < len; i++){
6772             m[i].reject();
6773         }
6774     },
6775
6776     onMetaChange : function(meta, rtype, o){
6777         this.recordType = rtype;
6778         this.fields = rtype.prototype.fields;
6779         delete this.snapshot;
6780         this.sortInfo = meta.sortInfo || this.sortInfo;
6781         this.modified = [];
6782         this.fireEvent('metachange', this, this.reader.meta);
6783     },
6784     
6785     moveIndex : function(data, type)
6786     {
6787         var index = this.indexOf(data);
6788         
6789         var newIndex = index + type;
6790         
6791         this.remove(data);
6792         
6793         this.insert(newIndex, data);
6794         
6795     }
6796 });/*
6797  * Based on:
6798  * Ext JS Library 1.1.1
6799  * Copyright(c) 2006-2007, Ext JS, LLC.
6800  *
6801  * Originally Released Under LGPL - original licence link has changed is not relivant.
6802  *
6803  * Fork - LGPL
6804  * <script type="text/javascript">
6805  */
6806
6807 /**
6808  * @class Roo.data.SimpleStore
6809  * @extends Roo.data.Store
6810  * Small helper class to make creating Stores from Array data easier.
6811  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6812  * @cfg {Array} fields An array of field definition objects, or field name strings.
6813  * @cfg {Array} data The multi-dimensional array of data
6814  * @constructor
6815  * @param {Object} config
6816  */
6817 Roo.data.SimpleStore = function(config){
6818     Roo.data.SimpleStore.superclass.constructor.call(this, {
6819         isLocal : true,
6820         reader: new Roo.data.ArrayReader({
6821                 id: config.id
6822             },
6823             Roo.data.Record.create(config.fields)
6824         ),
6825         proxy : new Roo.data.MemoryProxy(config.data)
6826     });
6827     this.load();
6828 };
6829 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6830  * Based on:
6831  * Ext JS Library 1.1.1
6832  * Copyright(c) 2006-2007, Ext JS, LLC.
6833  *
6834  * Originally Released Under LGPL - original licence link has changed is not relivant.
6835  *
6836  * Fork - LGPL
6837  * <script type="text/javascript">
6838  */
6839
6840 /**
6841 /**
6842  * @extends Roo.data.Store
6843  * @class Roo.data.JsonStore
6844  * Small helper class to make creating Stores for JSON data easier. <br/>
6845 <pre><code>
6846 var store = new Roo.data.JsonStore({
6847     url: 'get-images.php',
6848     root: 'images',
6849     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6850 });
6851 </code></pre>
6852  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6853  * JsonReader and HttpProxy (unless inline data is provided).</b>
6854  * @cfg {Array} fields An array of field definition objects, or field name strings.
6855  * @constructor
6856  * @param {Object} config
6857  */
6858 Roo.data.JsonStore = function(c){
6859     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6860         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6861         reader: new Roo.data.JsonReader(c, c.fields)
6862     }));
6863 };
6864 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6865  * Based on:
6866  * Ext JS Library 1.1.1
6867  * Copyright(c) 2006-2007, Ext JS, LLC.
6868  *
6869  * Originally Released Under LGPL - original licence link has changed is not relivant.
6870  *
6871  * Fork - LGPL
6872  * <script type="text/javascript">
6873  */
6874
6875  
6876 Roo.data.Field = function(config){
6877     if(typeof config == "string"){
6878         config = {name: config};
6879     }
6880     Roo.apply(this, config);
6881     
6882     if(!this.type){
6883         this.type = "auto";
6884     }
6885     
6886     var st = Roo.data.SortTypes;
6887     // named sortTypes are supported, here we look them up
6888     if(typeof this.sortType == "string"){
6889         this.sortType = st[this.sortType];
6890     }
6891     
6892     // set default sortType for strings and dates
6893     if(!this.sortType){
6894         switch(this.type){
6895             case "string":
6896                 this.sortType = st.asUCString;
6897                 break;
6898             case "date":
6899                 this.sortType = st.asDate;
6900                 break;
6901             default:
6902                 this.sortType = st.none;
6903         }
6904     }
6905
6906     // define once
6907     var stripRe = /[\$,%]/g;
6908
6909     // prebuilt conversion function for this field, instead of
6910     // switching every time we're reading a value
6911     if(!this.convert){
6912         var cv, dateFormat = this.dateFormat;
6913         switch(this.type){
6914             case "":
6915             case "auto":
6916             case undefined:
6917                 cv = function(v){ return v; };
6918                 break;
6919             case "string":
6920                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6921                 break;
6922             case "int":
6923                 cv = function(v){
6924                     return v !== undefined && v !== null && v !== '' ?
6925                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6926                     };
6927                 break;
6928             case "float":
6929                 cv = function(v){
6930                     return v !== undefined && v !== null && v !== '' ?
6931                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6932                     };
6933                 break;
6934             case "bool":
6935             case "boolean":
6936                 cv = function(v){ return v === true || v === "true" || v == 1; };
6937                 break;
6938             case "date":
6939                 cv = function(v){
6940                     if(!v){
6941                         return '';
6942                     }
6943                     if(v instanceof Date){
6944                         return v;
6945                     }
6946                     if(dateFormat){
6947                         if(dateFormat == "timestamp"){
6948                             return new Date(v*1000);
6949                         }
6950                         return Date.parseDate(v, dateFormat);
6951                     }
6952                     var parsed = Date.parse(v);
6953                     return parsed ? new Date(parsed) : null;
6954                 };
6955              break;
6956             
6957         }
6958         this.convert = cv;
6959     }
6960 };
6961
6962 Roo.data.Field.prototype = {
6963     dateFormat: null,
6964     defaultValue: "",
6965     mapping: null,
6966     sortType : null,
6967     sortDir : "ASC"
6968 };/*
6969  * Based on:
6970  * Ext JS Library 1.1.1
6971  * Copyright(c) 2006-2007, Ext JS, LLC.
6972  *
6973  * Originally Released Under LGPL - original licence link has changed is not relivant.
6974  *
6975  * Fork - LGPL
6976  * <script type="text/javascript">
6977  */
6978  
6979 // Base class for reading structured data from a data source.  This class is intended to be
6980 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6981
6982 /**
6983  * @class Roo.data.DataReader
6984  * Base class for reading structured data from a data source.  This class is intended to be
6985  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6986  */
6987
6988 Roo.data.DataReader = function(meta, recordType){
6989     
6990     this.meta = meta;
6991     
6992     this.recordType = recordType instanceof Array ? 
6993         Roo.data.Record.create(recordType) : recordType;
6994 };
6995
6996 Roo.data.DataReader.prototype = {
6997      /**
6998      * Create an empty record
6999      * @param {Object} data (optional) - overlay some values
7000      * @return {Roo.data.Record} record created.
7001      */
7002     newRow :  function(d) {
7003         var da =  {};
7004         this.recordType.prototype.fields.each(function(c) {
7005             switch( c.type) {
7006                 case 'int' : da[c.name] = 0; break;
7007                 case 'date' : da[c.name] = new Date(); break;
7008                 case 'float' : da[c.name] = 0.0; break;
7009                 case 'boolean' : da[c.name] = false; break;
7010                 default : da[c.name] = ""; break;
7011             }
7012             
7013         });
7014         return new this.recordType(Roo.apply(da, d));
7015     }
7016     
7017 };/*
7018  * Based on:
7019  * Ext JS Library 1.1.1
7020  * Copyright(c) 2006-2007, Ext JS, LLC.
7021  *
7022  * Originally Released Under LGPL - original licence link has changed is not relivant.
7023  *
7024  * Fork - LGPL
7025  * <script type="text/javascript">
7026  */
7027
7028 /**
7029  * @class Roo.data.DataProxy
7030  * @extends Roo.data.Observable
7031  * This class is an abstract base class for implementations which provide retrieval of
7032  * unformatted data objects.<br>
7033  * <p>
7034  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7035  * (of the appropriate type which knows how to parse the data object) to provide a block of
7036  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7037  * <p>
7038  * Custom implementations must implement the load method as described in
7039  * {@link Roo.data.HttpProxy#load}.
7040  */
7041 Roo.data.DataProxy = function(){
7042     this.addEvents({
7043         /**
7044          * @event beforeload
7045          * Fires before a network request is made to retrieve a data object.
7046          * @param {Object} This DataProxy object.
7047          * @param {Object} params The params parameter to the load function.
7048          */
7049         beforeload : true,
7050         /**
7051          * @event load
7052          * Fires before the load method's callback is called.
7053          * @param {Object} This DataProxy object.
7054          * @param {Object} o The data object.
7055          * @param {Object} arg The callback argument object passed to the load function.
7056          */
7057         load : true,
7058         /**
7059          * @event loadexception
7060          * Fires if an Exception occurs during data retrieval.
7061          * @param {Object} This DataProxy object.
7062          * @param {Object} o The data object.
7063          * @param {Object} arg The callback argument object passed to the load function.
7064          * @param {Object} e The Exception.
7065          */
7066         loadexception : true
7067     });
7068     Roo.data.DataProxy.superclass.constructor.call(this);
7069 };
7070
7071 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7072
7073     /**
7074      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7075      */
7076 /*
7077  * Based on:
7078  * Ext JS Library 1.1.1
7079  * Copyright(c) 2006-2007, Ext JS, LLC.
7080  *
7081  * Originally Released Under LGPL - original licence link has changed is not relivant.
7082  *
7083  * Fork - LGPL
7084  * <script type="text/javascript">
7085  */
7086 /**
7087  * @class Roo.data.MemoryProxy
7088  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7089  * to the Reader when its load method is called.
7090  * @constructor
7091  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7092  */
7093 Roo.data.MemoryProxy = function(data){
7094     if (data.data) {
7095         data = data.data;
7096     }
7097     Roo.data.MemoryProxy.superclass.constructor.call(this);
7098     this.data = data;
7099 };
7100
7101 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7102     /**
7103      * Load data from the requested source (in this case an in-memory
7104      * data object passed to the constructor), read the data object into
7105      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7106      * process that block using the passed callback.
7107      * @param {Object} params This parameter is not used by the MemoryProxy class.
7108      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7109      * object into a block of Roo.data.Records.
7110      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7111      * The function must be passed <ul>
7112      * <li>The Record block object</li>
7113      * <li>The "arg" argument from the load function</li>
7114      * <li>A boolean success indicator</li>
7115      * </ul>
7116      * @param {Object} scope The scope in which to call the callback
7117      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7118      */
7119     load : function(params, reader, callback, scope, arg){
7120         params = params || {};
7121         var result;
7122         try {
7123             result = reader.readRecords(this.data);
7124         }catch(e){
7125             this.fireEvent("loadexception", this, arg, null, e);
7126             callback.call(scope, null, arg, false);
7127             return;
7128         }
7129         callback.call(scope, result, arg, true);
7130     },
7131     
7132     // private
7133     update : function(params, records){
7134         
7135     }
7136 });/*
7137  * Based on:
7138  * Ext JS Library 1.1.1
7139  * Copyright(c) 2006-2007, Ext JS, LLC.
7140  *
7141  * Originally Released Under LGPL - original licence link has changed is not relivant.
7142  *
7143  * Fork - LGPL
7144  * <script type="text/javascript">
7145  */
7146 /**
7147  * @class Roo.data.HttpProxy
7148  * @extends Roo.data.DataProxy
7149  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7150  * configured to reference a certain URL.<br><br>
7151  * <p>
7152  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7153  * from which the running page was served.<br><br>
7154  * <p>
7155  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7156  * <p>
7157  * Be aware that to enable the browser to parse an XML document, the server must set
7158  * the Content-Type header in the HTTP response to "text/xml".
7159  * @constructor
7160  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7161  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7162  * will be used to make the request.
7163  */
7164 Roo.data.HttpProxy = function(conn){
7165     Roo.data.HttpProxy.superclass.constructor.call(this);
7166     // is conn a conn config or a real conn?
7167     this.conn = conn;
7168     this.useAjax = !conn || !conn.events;
7169   
7170 };
7171
7172 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7173     // thse are take from connection...
7174     
7175     /**
7176      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7177      */
7178     /**
7179      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7180      * extra parameters to each request made by this object. (defaults to undefined)
7181      */
7182     /**
7183      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7184      *  to each request made by this object. (defaults to undefined)
7185      */
7186     /**
7187      * @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)
7188      */
7189     /**
7190      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7191      */
7192      /**
7193      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7194      * @type Boolean
7195      */
7196   
7197
7198     /**
7199      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7200      * @type Boolean
7201      */
7202     /**
7203      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7204      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7205      * a finer-grained basis than the DataProxy events.
7206      */
7207     getConnection : function(){
7208         return this.useAjax ? Roo.Ajax : this.conn;
7209     },
7210
7211     /**
7212      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7213      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7214      * process that block using the passed callback.
7215      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7216      * for the request to the remote server.
7217      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7218      * object into a block of Roo.data.Records.
7219      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7220      * The function must be passed <ul>
7221      * <li>The Record block object</li>
7222      * <li>The "arg" argument from the load function</li>
7223      * <li>A boolean success indicator</li>
7224      * </ul>
7225      * @param {Object} scope The scope in which to call the callback
7226      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7227      */
7228     load : function(params, reader, callback, scope, arg){
7229         if(this.fireEvent("beforeload", this, params) !== false){
7230             var  o = {
7231                 params : params || {},
7232                 request: {
7233                     callback : callback,
7234                     scope : scope,
7235                     arg : arg
7236                 },
7237                 reader: reader,
7238                 callback : this.loadResponse,
7239                 scope: this
7240             };
7241             if(this.useAjax){
7242                 Roo.applyIf(o, this.conn);
7243                 if(this.activeRequest){
7244                     Roo.Ajax.abort(this.activeRequest);
7245                 }
7246                 this.activeRequest = Roo.Ajax.request(o);
7247             }else{
7248                 this.conn.request(o);
7249             }
7250         }else{
7251             callback.call(scope||this, null, arg, false);
7252         }
7253     },
7254
7255     // private
7256     loadResponse : function(o, success, response){
7257         delete this.activeRequest;
7258         if(!success){
7259             this.fireEvent("loadexception", this, o, response);
7260             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7261             return;
7262         }
7263         var result;
7264         try {
7265             result = o.reader.read(response);
7266         }catch(e){
7267             this.fireEvent("loadexception", this, o, response, e);
7268             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7269             return;
7270         }
7271         
7272         this.fireEvent("load", this, o, o.request.arg);
7273         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7274     },
7275
7276     // private
7277     update : function(dataSet){
7278
7279     },
7280
7281     // private
7282     updateResponse : function(dataSet){
7283
7284     }
7285 });/*
7286  * Based on:
7287  * Ext JS Library 1.1.1
7288  * Copyright(c) 2006-2007, Ext JS, LLC.
7289  *
7290  * Originally Released Under LGPL - original licence link has changed is not relivant.
7291  *
7292  * Fork - LGPL
7293  * <script type="text/javascript">
7294  */
7295
7296 /**
7297  * @class Roo.data.ScriptTagProxy
7298  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7299  * other than the originating domain of the running page.<br><br>
7300  * <p>
7301  * <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
7302  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7303  * <p>
7304  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7305  * source code that is used as the source inside a &lt;script> tag.<br><br>
7306  * <p>
7307  * In order for the browser to process the returned data, the server must wrap the data object
7308  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7309  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7310  * depending on whether the callback name was passed:
7311  * <p>
7312  * <pre><code>
7313 boolean scriptTag = false;
7314 String cb = request.getParameter("callback");
7315 if (cb != null) {
7316     scriptTag = true;
7317     response.setContentType("text/javascript");
7318 } else {
7319     response.setContentType("application/x-json");
7320 }
7321 Writer out = response.getWriter();
7322 if (scriptTag) {
7323     out.write(cb + "(");
7324 }
7325 out.print(dataBlock.toJsonString());
7326 if (scriptTag) {
7327     out.write(");");
7328 }
7329 </pre></code>
7330  *
7331  * @constructor
7332  * @param {Object} config A configuration object.
7333  */
7334 Roo.data.ScriptTagProxy = function(config){
7335     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7336     Roo.apply(this, config);
7337     this.head = document.getElementsByTagName("head")[0];
7338 };
7339
7340 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7341
7342 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7343     /**
7344      * @cfg {String} url The URL from which to request the data object.
7345      */
7346     /**
7347      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7348      */
7349     timeout : 30000,
7350     /**
7351      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7352      * the server the name of the callback function set up by the load call to process the returned data object.
7353      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7354      * javascript output which calls this named function passing the data object as its only parameter.
7355      */
7356     callbackParam : "callback",
7357     /**
7358      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7359      * name to the request.
7360      */
7361     nocache : true,
7362
7363     /**
7364      * Load data from the configured URL, read the data object into
7365      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7366      * process that block using the passed callback.
7367      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7368      * for the request to the remote server.
7369      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7370      * object into a block of Roo.data.Records.
7371      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7372      * The function must be passed <ul>
7373      * <li>The Record block object</li>
7374      * <li>The "arg" argument from the load function</li>
7375      * <li>A boolean success indicator</li>
7376      * </ul>
7377      * @param {Object} scope The scope in which to call the callback
7378      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7379      */
7380     load : function(params, reader, callback, scope, arg){
7381         if(this.fireEvent("beforeload", this, params) !== false){
7382
7383             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7384
7385             var url = this.url;
7386             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7387             if(this.nocache){
7388                 url += "&_dc=" + (new Date().getTime());
7389             }
7390             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7391             var trans = {
7392                 id : transId,
7393                 cb : "stcCallback"+transId,
7394                 scriptId : "stcScript"+transId,
7395                 params : params,
7396                 arg : arg,
7397                 url : url,
7398                 callback : callback,
7399                 scope : scope,
7400                 reader : reader
7401             };
7402             var conn = this;
7403
7404             window[trans.cb] = function(o){
7405                 conn.handleResponse(o, trans);
7406             };
7407
7408             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7409
7410             if(this.autoAbort !== false){
7411                 this.abort();
7412             }
7413
7414             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7415
7416             var script = document.createElement("script");
7417             script.setAttribute("src", url);
7418             script.setAttribute("type", "text/javascript");
7419             script.setAttribute("id", trans.scriptId);
7420             this.head.appendChild(script);
7421
7422             this.trans = trans;
7423         }else{
7424             callback.call(scope||this, null, arg, false);
7425         }
7426     },
7427
7428     // private
7429     isLoading : function(){
7430         return this.trans ? true : false;
7431     },
7432
7433     /**
7434      * Abort the current server request.
7435      */
7436     abort : function(){
7437         if(this.isLoading()){
7438             this.destroyTrans(this.trans);
7439         }
7440     },
7441
7442     // private
7443     destroyTrans : function(trans, isLoaded){
7444         this.head.removeChild(document.getElementById(trans.scriptId));
7445         clearTimeout(trans.timeoutId);
7446         if(isLoaded){
7447             window[trans.cb] = undefined;
7448             try{
7449                 delete window[trans.cb];
7450             }catch(e){}
7451         }else{
7452             // if hasn't been loaded, wait for load to remove it to prevent script error
7453             window[trans.cb] = function(){
7454                 window[trans.cb] = undefined;
7455                 try{
7456                     delete window[trans.cb];
7457                 }catch(e){}
7458             };
7459         }
7460     },
7461
7462     // private
7463     handleResponse : function(o, trans){
7464         this.trans = false;
7465         this.destroyTrans(trans, true);
7466         var result;
7467         try {
7468             result = trans.reader.readRecords(o);
7469         }catch(e){
7470             this.fireEvent("loadexception", this, o, trans.arg, e);
7471             trans.callback.call(trans.scope||window, null, trans.arg, false);
7472             return;
7473         }
7474         this.fireEvent("load", this, o, trans.arg);
7475         trans.callback.call(trans.scope||window, result, trans.arg, true);
7476     },
7477
7478     // private
7479     handleFailure : function(trans){
7480         this.trans = false;
7481         this.destroyTrans(trans, false);
7482         this.fireEvent("loadexception", this, null, trans.arg);
7483         trans.callback.call(trans.scope||window, null, trans.arg, false);
7484     }
7485 });/*
7486  * Based on:
7487  * Ext JS Library 1.1.1
7488  * Copyright(c) 2006-2007, Ext JS, LLC.
7489  *
7490  * Originally Released Under LGPL - original licence link has changed is not relivant.
7491  *
7492  * Fork - LGPL
7493  * <script type="text/javascript">
7494  */
7495
7496 /**
7497  * @class Roo.data.JsonReader
7498  * @extends Roo.data.DataReader
7499  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7500  * based on mappings in a provided Roo.data.Record constructor.
7501  * 
7502  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7503  * in the reply previously. 
7504  * 
7505  * <p>
7506  * Example code:
7507  * <pre><code>
7508 var RecordDef = Roo.data.Record.create([
7509     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7510     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7511 ]);
7512 var myReader = new Roo.data.JsonReader({
7513     totalProperty: "results",    // The property which contains the total dataset size (optional)
7514     root: "rows",                // The property which contains an Array of row objects
7515     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7516 }, RecordDef);
7517 </code></pre>
7518  * <p>
7519  * This would consume a JSON file like this:
7520  * <pre><code>
7521 { 'results': 2, 'rows': [
7522     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7523     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7524 }
7525 </code></pre>
7526  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7527  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7528  * paged from the remote server.
7529  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7530  * @cfg {String} root name of the property which contains the Array of row objects.
7531  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7532  * @constructor
7533  * Create a new JsonReader
7534  * @param {Object} meta Metadata configuration options
7535  * @param {Object} recordType Either an Array of field definition objects,
7536  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7537  */
7538 Roo.data.JsonReader = function(meta, recordType){
7539     
7540     meta = meta || {};
7541     // set some defaults:
7542     Roo.applyIf(meta, {
7543         totalProperty: 'total',
7544         successProperty : 'success',
7545         root : 'data',
7546         id : 'id'
7547     });
7548     
7549     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7550 };
7551 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7552     
7553     /**
7554      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7555      * Used by Store query builder to append _requestMeta to params.
7556      * 
7557      */
7558     metaFromRemote : false,
7559     /**
7560      * This method is only used by a DataProxy which has retrieved data from a remote server.
7561      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7562      * @return {Object} data A data block which is used by an Roo.data.Store object as
7563      * a cache of Roo.data.Records.
7564      */
7565     read : function(response){
7566         var json = response.responseText;
7567        
7568         var o = /* eval:var:o */ eval("("+json+")");
7569         if(!o) {
7570             throw {message: "JsonReader.read: Json object not found"};
7571         }
7572         
7573         if(o.metaData){
7574             
7575             delete this.ef;
7576             this.metaFromRemote = true;
7577             this.meta = o.metaData;
7578             this.recordType = Roo.data.Record.create(o.metaData.fields);
7579             this.onMetaChange(this.meta, this.recordType, o);
7580         }
7581         return this.readRecords(o);
7582     },
7583
7584     // private function a store will implement
7585     onMetaChange : function(meta, recordType, o){
7586
7587     },
7588
7589     /**
7590          * @ignore
7591          */
7592     simpleAccess: function(obj, subsc) {
7593         return obj[subsc];
7594     },
7595
7596         /**
7597          * @ignore
7598          */
7599     getJsonAccessor: function(){
7600         var re = /[\[\.]/;
7601         return function(expr) {
7602             try {
7603                 return(re.test(expr))
7604                     ? new Function("obj", "return obj." + expr)
7605                     : function(obj){
7606                         return obj[expr];
7607                     };
7608             } catch(e){}
7609             return Roo.emptyFn;
7610         };
7611     }(),
7612
7613     /**
7614      * Create a data block containing Roo.data.Records from an XML document.
7615      * @param {Object} o An object which contains an Array of row objects in the property specified
7616      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7617      * which contains the total size of the dataset.
7618      * @return {Object} data A data block which is used by an Roo.data.Store object as
7619      * a cache of Roo.data.Records.
7620      */
7621     readRecords : function(o){
7622         /**
7623          * After any data loads, the raw JSON data is available for further custom processing.
7624          * @type Object
7625          */
7626         this.o = o;
7627         var s = this.meta, Record = this.recordType,
7628             f = Record.prototype.fields, fi = f.items, fl = f.length;
7629
7630 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7631         if (!this.ef) {
7632             if(s.totalProperty) {
7633                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7634                 }
7635                 if(s.successProperty) {
7636                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7637                 }
7638                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7639                 if (s.id) {
7640                         var g = this.getJsonAccessor(s.id);
7641                         this.getId = function(rec) {
7642                                 var r = g(rec);
7643                                 return (r === undefined || r === "") ? null : r;
7644                         };
7645                 } else {
7646                         this.getId = function(){return null;};
7647                 }
7648             this.ef = [];
7649             for(var jj = 0; jj < fl; jj++){
7650                 f = fi[jj];
7651                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7652                 this.ef[jj] = this.getJsonAccessor(map);
7653             }
7654         }
7655
7656         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7657         if(s.totalProperty){
7658             var vt = parseInt(this.getTotal(o), 10);
7659             if(!isNaN(vt)){
7660                 totalRecords = vt;
7661             }
7662         }
7663         if(s.successProperty){
7664             var vs = this.getSuccess(o);
7665             if(vs === false || vs === 'false'){
7666                 success = false;
7667             }
7668         }
7669         var records = [];
7670             for(var i = 0; i < c; i++){
7671                     var n = root[i];
7672                 var values = {};
7673                 var id = this.getId(n);
7674                 for(var j = 0; j < fl; j++){
7675                     f = fi[j];
7676                 var v = this.ef[j](n);
7677                 if (!f.convert) {
7678                     Roo.log('missing convert for ' + f.name);
7679                     Roo.log(f);
7680                     continue;
7681                 }
7682                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7683                 }
7684                 var record = new Record(values, id);
7685                 record.json = n;
7686                 records[i] = record;
7687             }
7688             return {
7689             raw : o,
7690                 success : success,
7691                 records : records,
7692                 totalRecords : totalRecords
7693             };
7694     }
7695 });/*
7696  * Based on:
7697  * Ext JS Library 1.1.1
7698  * Copyright(c) 2006-2007, Ext JS, LLC.
7699  *
7700  * Originally Released Under LGPL - original licence link has changed is not relivant.
7701  *
7702  * Fork - LGPL
7703  * <script type="text/javascript">
7704  */
7705
7706 /**
7707  * @class Roo.data.ArrayReader
7708  * @extends Roo.data.DataReader
7709  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7710  * Each element of that Array represents a row of data fields. The
7711  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7712  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7713  * <p>
7714  * Example code:.
7715  * <pre><code>
7716 var RecordDef = Roo.data.Record.create([
7717     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7718     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7719 ]);
7720 var myReader = new Roo.data.ArrayReader({
7721     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7722 }, RecordDef);
7723 </code></pre>
7724  * <p>
7725  * This would consume an Array like this:
7726  * <pre><code>
7727 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7728   </code></pre>
7729  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7730  * @constructor
7731  * Create a new JsonReader
7732  * @param {Object} meta Metadata configuration options.
7733  * @param {Object} recordType Either an Array of field definition objects
7734  * as specified to {@link Roo.data.Record#create},
7735  * or an {@link Roo.data.Record} object
7736  * created using {@link Roo.data.Record#create}.
7737  */
7738 Roo.data.ArrayReader = function(meta, recordType){
7739     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7740 };
7741
7742 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7743     /**
7744      * Create a data block containing Roo.data.Records from an XML document.
7745      * @param {Object} o An Array of row objects which represents the dataset.
7746      * @return {Object} data A data block which is used by an Roo.data.Store object as
7747      * a cache of Roo.data.Records.
7748      */
7749     readRecords : function(o){
7750         var sid = this.meta ? this.meta.id : null;
7751         var recordType = this.recordType, fields = recordType.prototype.fields;
7752         var records = [];
7753         var root = o;
7754             for(var i = 0; i < root.length; i++){
7755                     var n = root[i];
7756                 var values = {};
7757                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7758                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7759                 var f = fields.items[j];
7760                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7761                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7762                 v = f.convert(v);
7763                 values[f.name] = v;
7764             }
7765                 var record = new recordType(values, id);
7766                 record.json = n;
7767                 records[records.length] = record;
7768             }
7769             return {
7770                 records : records,
7771                 totalRecords : records.length
7772             };
7773     }
7774 });/*
7775  * - LGPL
7776  * * 
7777  */
7778
7779 /**
7780  * @class Roo.bootstrap.ComboBox
7781  * @extends Roo.bootstrap.TriggerField
7782  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7783  * @cfg {Boolean} append (true|false) default false
7784  * @constructor
7785  * Create a new ComboBox.
7786  * @param {Object} config Configuration options
7787  */
7788 Roo.bootstrap.ComboBox = function(config){
7789     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7790     this.addEvents({
7791         /**
7792          * @event expand
7793          * Fires when the dropdown list is expanded
7794              * @param {Roo.bootstrap.ComboBox} combo This combo box
7795              */
7796         'expand' : true,
7797         /**
7798          * @event collapse
7799          * Fires when the dropdown list is collapsed
7800              * @param {Roo.bootstrap.ComboBox} combo This combo box
7801              */
7802         'collapse' : true,
7803         /**
7804          * @event beforeselect
7805          * Fires before a list item is selected. Return false to cancel the selection.
7806              * @param {Roo.bootstrap.ComboBox} combo This combo box
7807              * @param {Roo.data.Record} record The data record returned from the underlying store
7808              * @param {Number} index The index of the selected item in the dropdown list
7809              */
7810         'beforeselect' : true,
7811         /**
7812          * @event select
7813          * Fires when a list item is selected
7814              * @param {Roo.bootstrap.ComboBox} combo This combo box
7815              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7816              * @param {Number} index The index of the selected item in the dropdown list
7817              */
7818         'select' : true,
7819         /**
7820          * @event beforequery
7821          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7822          * The event object passed has these properties:
7823              * @param {Roo.bootstrap.ComboBox} combo This combo box
7824              * @param {String} query The query
7825              * @param {Boolean} forceAll true to force "all" query
7826              * @param {Boolean} cancel true to cancel the query
7827              * @param {Object} e The query event object
7828              */
7829         'beforequery': true,
7830          /**
7831          * @event add
7832          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7833              * @param {Roo.bootstrap.ComboBox} combo This combo box
7834              */
7835         'add' : true,
7836         /**
7837          * @event edit
7838          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7839              * @param {Roo.bootstrap.ComboBox} combo This combo box
7840              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7841              */
7842         'edit' : true,
7843         /**
7844          * @event remove
7845          * Fires when the remove value from the combobox array
7846              * @param {Roo.bootstrap.ComboBox} combo This combo box
7847              */
7848         'remove' : true
7849         
7850     });
7851     
7852     
7853     this.selectedIndex = -1;
7854     if(this.mode == 'local'){
7855         if(config.queryDelay === undefined){
7856             this.queryDelay = 10;
7857         }
7858         if(config.minChars === undefined){
7859             this.minChars = 0;
7860         }
7861     }
7862 };
7863
7864 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7865      
7866     /**
7867      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7868      * rendering into an Roo.Editor, defaults to false)
7869      */
7870     /**
7871      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7872      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7873      */
7874     /**
7875      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7876      */
7877     /**
7878      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7879      * the dropdown list (defaults to undefined, with no header element)
7880      */
7881
7882      /**
7883      * @cfg {String/Roo.Template} tpl The template to use to render the output
7884      */
7885      
7886      /**
7887      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7888      */
7889     listWidth: undefined,
7890     /**
7891      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7892      * mode = 'remote' or 'text' if mode = 'local')
7893      */
7894     displayField: undefined,
7895     /**
7896      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7897      * mode = 'remote' or 'value' if mode = 'local'). 
7898      * Note: use of a valueField requires the user make a selection
7899      * in order for a value to be mapped.
7900      */
7901     valueField: undefined,
7902     
7903     
7904     /**
7905      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7906      * field's data value (defaults to the underlying DOM element's name)
7907      */
7908     hiddenName: undefined,
7909     /**
7910      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7911      */
7912     listClass: '',
7913     /**
7914      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7915      */
7916     selectedClass: 'active',
7917     
7918     /**
7919      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7920      */
7921     shadow:'sides',
7922     /**
7923      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7924      * anchor positions (defaults to 'tl-bl')
7925      */
7926     listAlign: 'tl-bl?',
7927     /**
7928      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7929      */
7930     maxHeight: 300,
7931     /**
7932      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7933      * query specified by the allQuery config option (defaults to 'query')
7934      */
7935     triggerAction: 'query',
7936     /**
7937      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7938      * (defaults to 4, does not apply if editable = false)
7939      */
7940     minChars : 4,
7941     /**
7942      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7943      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7944      */
7945     typeAhead: false,
7946     /**
7947      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7948      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7949      */
7950     queryDelay: 500,
7951     /**
7952      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7953      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7954      */
7955     pageSize: 0,
7956     /**
7957      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7958      * when editable = true (defaults to false)
7959      */
7960     selectOnFocus:false,
7961     /**
7962      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7963      */
7964     queryParam: 'query',
7965     /**
7966      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7967      * when mode = 'remote' (defaults to 'Loading...')
7968      */
7969     loadingText: 'Loading...',
7970     /**
7971      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7972      */
7973     resizable: false,
7974     /**
7975      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7976      */
7977     handleHeight : 8,
7978     /**
7979      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7980      * traditional select (defaults to true)
7981      */
7982     editable: true,
7983     /**
7984      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7985      */
7986     allQuery: '',
7987     /**
7988      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7989      */
7990     mode: 'remote',
7991     /**
7992      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7993      * listWidth has a higher value)
7994      */
7995     minListWidth : 70,
7996     /**
7997      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7998      * allow the user to set arbitrary text into the field (defaults to false)
7999      */
8000     forceSelection:false,
8001     /**
8002      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8003      * if typeAhead = true (defaults to 250)
8004      */
8005     typeAheadDelay : 250,
8006     /**
8007      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8008      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8009      */
8010     valueNotFoundText : undefined,
8011     /**
8012      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8013      */
8014     blockFocus : false,
8015     
8016     /**
8017      * @cfg {Boolean} disableClear Disable showing of clear button.
8018      */
8019     disableClear : false,
8020     /**
8021      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8022      */
8023     alwaysQuery : false,
8024     
8025     /**
8026      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8027      */
8028     multiple : false,
8029     
8030     //private
8031     addicon : false,
8032     editicon: false,
8033     
8034     page: 0,
8035     hasQuery: false,
8036     append: false,
8037     loadNext: false,
8038     item: [],
8039     
8040     // element that contains real text value.. (when hidden is used..)
8041      
8042     // private
8043     initEvents: function(){
8044         
8045         if (!this.store) {
8046             throw "can not find store for combo";
8047         }
8048         this.store = Roo.factory(this.store, Roo.data);
8049         
8050         
8051         
8052         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8053         
8054         
8055         if(this.hiddenName){
8056             
8057             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8058             
8059             this.hiddenField.dom.value =
8060                 this.hiddenValue !== undefined ? this.hiddenValue :
8061                 this.value !== undefined ? this.value : '';
8062
8063             // prevent input submission
8064             this.el.dom.removeAttribute('name');
8065             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8066              
8067              
8068         }
8069         //if(Roo.isGecko){
8070         //    this.el.dom.setAttribute('autocomplete', 'off');
8071         //}
8072
8073         var cls = 'x-combo-list';
8074         this.list = this.el.select('ul.dropdown-menu',true).first();
8075
8076         //this.list = new Roo.Layer({
8077         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8078         //});
8079         
8080         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8081         this.list.setWidth(lw);
8082         
8083         this.list.on('mouseover', this.onViewOver, this);
8084         this.list.on('mousemove', this.onViewMove, this);
8085         
8086         this.list.on('scroll', this.onViewScroll, this);
8087         
8088         /*
8089         this.list.swallowEvent('mousewheel');
8090         this.assetHeight = 0;
8091
8092         if(this.title){
8093             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8094             this.assetHeight += this.header.getHeight();
8095         }
8096
8097         this.innerList = this.list.createChild({cls:cls+'-inner'});
8098         this.innerList.on('mouseover', this.onViewOver, this);
8099         this.innerList.on('mousemove', this.onViewMove, this);
8100         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8101         
8102         if(this.allowBlank && !this.pageSize && !this.disableClear){
8103             this.footer = this.list.createChild({cls:cls+'-ft'});
8104             this.pageTb = new Roo.Toolbar(this.footer);
8105            
8106         }
8107         if(this.pageSize){
8108             this.footer = this.list.createChild({cls:cls+'-ft'});
8109             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8110                     {pageSize: this.pageSize});
8111             
8112         }
8113         
8114         if (this.pageTb && this.allowBlank && !this.disableClear) {
8115             var _this = this;
8116             this.pageTb.add(new Roo.Toolbar.Fill(), {
8117                 cls: 'x-btn-icon x-btn-clear',
8118                 text: '&#160;',
8119                 handler: function()
8120                 {
8121                     _this.collapse();
8122                     _this.clearValue();
8123                     _this.onSelect(false, -1);
8124                 }
8125             });
8126         }
8127         if (this.footer) {
8128             this.assetHeight += this.footer.getHeight();
8129         }
8130         */
8131             
8132         if(!this.tpl){
8133             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8134         }
8135
8136         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8137             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8138         });
8139         //this.view.wrapEl.setDisplayed(false);
8140         this.view.on('click', this.onViewClick, this);
8141         
8142         
8143         
8144         this.store.on('beforeload', this.onBeforeLoad, this);
8145         this.store.on('load', this.onLoad, this);
8146         this.store.on('loadexception', this.onLoadException, this);
8147         /*
8148         if(this.resizable){
8149             this.resizer = new Roo.Resizable(this.list,  {
8150                pinned:true, handles:'se'
8151             });
8152             this.resizer.on('resize', function(r, w, h){
8153                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8154                 this.listWidth = w;
8155                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8156                 this.restrictHeight();
8157             }, this);
8158             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8159         }
8160         */
8161         if(!this.editable){
8162             this.editable = true;
8163             this.setEditable(false);
8164         }
8165         
8166         /*
8167         
8168         if (typeof(this.events.add.listeners) != 'undefined') {
8169             
8170             this.addicon = this.wrap.createChild(
8171                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8172        
8173             this.addicon.on('click', function(e) {
8174                 this.fireEvent('add', this);
8175             }, this);
8176         }
8177         if (typeof(this.events.edit.listeners) != 'undefined') {
8178             
8179             this.editicon = this.wrap.createChild(
8180                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8181             if (this.addicon) {
8182                 this.editicon.setStyle('margin-left', '40px');
8183             }
8184             this.editicon.on('click', function(e) {
8185                 
8186                 // we fire even  if inothing is selected..
8187                 this.fireEvent('edit', this, this.lastData );
8188                 
8189             }, this);
8190         }
8191         */
8192         
8193         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8194             "up" : function(e){
8195                 this.inKeyMode = true;
8196                 this.selectPrev();
8197             },
8198
8199             "down" : function(e){
8200                 if(!this.isExpanded()){
8201                     this.onTriggerClick();
8202                 }else{
8203                     this.inKeyMode = true;
8204                     this.selectNext();
8205                 }
8206             },
8207
8208             "enter" : function(e){
8209                 this.onViewClick();
8210                 //return true;
8211             },
8212
8213             "esc" : function(e){
8214                 this.collapse();
8215             },
8216
8217             "tab" : function(e){
8218                 this.collapse();
8219                 
8220                 if(this.fireEvent("specialkey", this, e)){
8221                     this.onViewClick(false);
8222                 }
8223                 
8224                 return true;
8225             },
8226
8227             scope : this,
8228
8229             doRelay : function(foo, bar, hname){
8230                 if(hname == 'down' || this.scope.isExpanded()){
8231                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8232                 }
8233                 return true;
8234             },
8235
8236             forceKeyDown: true
8237         });
8238         
8239         
8240         this.queryDelay = Math.max(this.queryDelay || 10,
8241                 this.mode == 'local' ? 10 : 250);
8242         
8243         
8244         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8245         
8246         if(this.typeAhead){
8247             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8248         }
8249         if(this.editable !== false){
8250             this.inputEl().on("keyup", this.onKeyUp, this);
8251         }
8252         if(this.forceSelection){
8253             this.on('blur', this.doForce, this);
8254         }
8255         
8256         if(this.multiple){
8257             this.choices = this.el.select('ul.select2-choices', true).first();
8258             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8259         }
8260     },
8261
8262     onDestroy : function(){
8263         if(this.view){
8264             this.view.setStore(null);
8265             this.view.el.removeAllListeners();
8266             this.view.el.remove();
8267             this.view.purgeListeners();
8268         }
8269         if(this.list){
8270             this.list.dom.innerHTML  = '';
8271         }
8272         if(this.store){
8273             this.store.un('beforeload', this.onBeforeLoad, this);
8274             this.store.un('load', this.onLoad, this);
8275             this.store.un('loadexception', this.onLoadException, this);
8276         }
8277         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8278     },
8279
8280     // private
8281     fireKey : function(e){
8282         if(e.isNavKeyPress() && !this.list.isVisible()){
8283             this.fireEvent("specialkey", this, e);
8284         }
8285     },
8286
8287     // private
8288     onResize: function(w, h){
8289 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8290 //        
8291 //        if(typeof w != 'number'){
8292 //            // we do not handle it!?!?
8293 //            return;
8294 //        }
8295 //        var tw = this.trigger.getWidth();
8296 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8297 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8298 //        var x = w - tw;
8299 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8300 //            
8301 //        //this.trigger.setStyle('left', x+'px');
8302 //        
8303 //        if(this.list && this.listWidth === undefined){
8304 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8305 //            this.list.setWidth(lw);
8306 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8307 //        }
8308         
8309     
8310         
8311     },
8312
8313     /**
8314      * Allow or prevent the user from directly editing the field text.  If false is passed,
8315      * the user will only be able to select from the items defined in the dropdown list.  This method
8316      * is the runtime equivalent of setting the 'editable' config option at config time.
8317      * @param {Boolean} value True to allow the user to directly edit the field text
8318      */
8319     setEditable : function(value){
8320         if(value == this.editable){
8321             return;
8322         }
8323         this.editable = value;
8324         if(!value){
8325             this.inputEl().dom.setAttribute('readOnly', true);
8326             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8327             this.inputEl().addClass('x-combo-noedit');
8328         }else{
8329             this.inputEl().dom.setAttribute('readOnly', false);
8330             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8331             this.inputEl().removeClass('x-combo-noedit');
8332         }
8333     },
8334
8335     // private
8336     
8337     onBeforeLoad : function(combo,opts){
8338         if(!this.hasFocus){
8339             return;
8340         }
8341          if (!opts.add) {
8342             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8343          }
8344         this.restrictHeight();
8345         this.selectedIndex = -1;
8346     },
8347
8348     // private
8349     onLoad : function(){
8350         
8351         this.hasQuery = false;
8352         
8353         if(!this.hasFocus){
8354             return;
8355         }
8356         
8357         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8358             this.loading.hide();
8359         }
8360         
8361         if(this.store.getCount() > 0){
8362             this.expand();
8363             this.restrictHeight();
8364             if(this.lastQuery == this.allQuery){
8365                 if(this.editable){
8366                     this.inputEl().dom.select();
8367                 }
8368                 if(!this.selectByValue(this.value, true)){
8369                     this.select(0, true);
8370                 }
8371             }else{
8372                 this.selectNext();
8373                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8374                     this.taTask.delay(this.typeAheadDelay);
8375                 }
8376             }
8377         }else{
8378             this.onEmptyResults();
8379         }
8380         
8381         //this.el.focus();
8382     },
8383     // private
8384     onLoadException : function()
8385     {
8386         this.hasQuery = false;
8387         
8388         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8389             this.loading.hide();
8390         }
8391         
8392         this.collapse();
8393         Roo.log(this.store.reader.jsonData);
8394         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8395             // fixme
8396             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8397         }
8398         
8399         
8400     },
8401     // private
8402     onTypeAhead : function(){
8403         if(this.store.getCount() > 0){
8404             var r = this.store.getAt(0);
8405             var newValue = r.data[this.displayField];
8406             var len = newValue.length;
8407             var selStart = this.getRawValue().length;
8408             
8409             if(selStart != len){
8410                 this.setRawValue(newValue);
8411                 this.selectText(selStart, newValue.length);
8412             }
8413         }
8414     },
8415
8416     // private
8417     onSelect : function(record, index){
8418         
8419         if(this.fireEvent('beforeselect', this, record, index) !== false){
8420         
8421             this.setFromData(index > -1 ? record.data : false);
8422             
8423             this.collapse();
8424             this.fireEvent('select', this, record, index);
8425         }
8426     },
8427
8428     /**
8429      * Returns the currently selected field value or empty string if no value is set.
8430      * @return {String} value The selected value
8431      */
8432     getValue : function(){
8433         
8434         if(this.multiple){
8435             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8436         }
8437         
8438         if(this.valueField){
8439             return typeof this.value != 'undefined' ? this.value : '';
8440         }else{
8441             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8442         }
8443     },
8444
8445     /**
8446      * Clears any text/value currently set in the field
8447      */
8448     clearValue : function(){
8449         if(this.hiddenField){
8450             this.hiddenField.dom.value = '';
8451         }
8452         this.value = '';
8453         this.setRawValue('');
8454         this.lastSelectionText = '';
8455         
8456     },
8457
8458     /**
8459      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8460      * will be displayed in the field.  If the value does not match the data value of an existing item,
8461      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8462      * Otherwise the field will be blank (although the value will still be set).
8463      * @param {String} value The value to match
8464      */
8465     setValue : function(v){
8466         if(this.multiple){
8467             this.syncValue();
8468             return;
8469         }
8470         
8471         var text = v;
8472         if(this.valueField){
8473             var r = this.findRecord(this.valueField, v);
8474             if(r){
8475                 text = r.data[this.displayField];
8476             }else if(this.valueNotFoundText !== undefined){
8477                 text = this.valueNotFoundText;
8478             }
8479         }
8480         this.lastSelectionText = text;
8481         if(this.hiddenField){
8482             this.hiddenField.dom.value = v;
8483         }
8484         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8485         this.value = v;
8486     },
8487     /**
8488      * @property {Object} the last set data for the element
8489      */
8490     
8491     lastData : false,
8492     /**
8493      * Sets the value of the field based on a object which is related to the record format for the store.
8494      * @param {Object} value the value to set as. or false on reset?
8495      */
8496     setFromData : function(o){
8497         
8498         if(this.multiple){
8499             this.addItem(o);
8500             return;
8501         }
8502             
8503         var dv = ''; // display value
8504         var vv = ''; // value value..
8505         this.lastData = o;
8506         if (this.displayField) {
8507             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8508         } else {
8509             // this is an error condition!!!
8510             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8511         }
8512         
8513         if(this.valueField){
8514             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8515         }
8516         
8517         if(this.hiddenField){
8518             this.hiddenField.dom.value = vv;
8519             
8520             this.lastSelectionText = dv;
8521             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8522             this.value = vv;
8523             return;
8524         }
8525         // no hidden field.. - we store the value in 'value', but still display
8526         // display field!!!!
8527         this.lastSelectionText = dv;
8528         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8529         this.value = vv;
8530         
8531         
8532     },
8533     // private
8534     reset : function(){
8535         // overridden so that last data is reset..
8536         this.setValue(this.originalValue);
8537         this.clearInvalid();
8538         this.lastData = false;
8539         if (this.view) {
8540             this.view.clearSelections();
8541         }
8542     },
8543     // private
8544     findRecord : function(prop, value){
8545         var record;
8546         if(this.store.getCount() > 0){
8547             this.store.each(function(r){
8548                 if(r.data[prop] == value){
8549                     record = r;
8550                     return false;
8551                 }
8552                 return true;
8553             });
8554         }
8555         return record;
8556     },
8557     
8558     getName: function()
8559     {
8560         // returns hidden if it's set..
8561         if (!this.rendered) {return ''};
8562         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8563         
8564     },
8565     // private
8566     onViewMove : function(e, t){
8567         this.inKeyMode = false;
8568     },
8569
8570     // private
8571     onViewOver : function(e, t){
8572         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8573             return;
8574         }
8575         var item = this.view.findItemFromChild(t);
8576         if(item){
8577             var index = this.view.indexOf(item);
8578             this.select(index, false);
8579         }
8580     },
8581
8582     // private
8583     onViewClick : function(doFocus)
8584     {
8585         var index = this.view.getSelectedIndexes()[0];
8586         var r = this.store.getAt(index);
8587         if(r){
8588             this.onSelect(r, index);
8589         }
8590         if(doFocus !== false && !this.blockFocus){
8591             this.inputEl().focus();
8592         }
8593     },
8594
8595     // private
8596     restrictHeight : function(){
8597         //this.innerList.dom.style.height = '';
8598         //var inner = this.innerList.dom;
8599         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8600         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8601         //this.list.beginUpdate();
8602         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8603         this.list.alignTo(this.inputEl(), this.listAlign);
8604         //this.list.endUpdate();
8605     },
8606
8607     // private
8608     onEmptyResults : function(){
8609         this.collapse();
8610     },
8611
8612     /**
8613      * Returns true if the dropdown list is expanded, else false.
8614      */
8615     isExpanded : function(){
8616         return this.list.isVisible();
8617     },
8618
8619     /**
8620      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8621      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8622      * @param {String} value The data value of the item to select
8623      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8624      * selected item if it is not currently in view (defaults to true)
8625      * @return {Boolean} True if the value matched an item in the list, else false
8626      */
8627     selectByValue : function(v, scrollIntoView){
8628         if(v !== undefined && v !== null){
8629             var r = this.findRecord(this.valueField || this.displayField, v);
8630             if(r){
8631                 this.select(this.store.indexOf(r), scrollIntoView);
8632                 return true;
8633             }
8634         }
8635         return false;
8636     },
8637
8638     /**
8639      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8640      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8641      * @param {Number} index The zero-based index of the list item to select
8642      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8643      * selected item if it is not currently in view (defaults to true)
8644      */
8645     select : function(index, scrollIntoView){
8646         this.selectedIndex = index;
8647         this.view.select(index);
8648         if(scrollIntoView !== false){
8649             var el = this.view.getNode(index);
8650             if(el){
8651                 //this.innerList.scrollChildIntoView(el, false);
8652                 
8653             }
8654         }
8655     },
8656
8657     // private
8658     selectNext : function(){
8659         var ct = this.store.getCount();
8660         if(ct > 0){
8661             if(this.selectedIndex == -1){
8662                 this.select(0);
8663             }else if(this.selectedIndex < ct-1){
8664                 this.select(this.selectedIndex+1);
8665             }
8666         }
8667     },
8668
8669     // private
8670     selectPrev : function(){
8671         var ct = this.store.getCount();
8672         if(ct > 0){
8673             if(this.selectedIndex == -1){
8674                 this.select(0);
8675             }else if(this.selectedIndex != 0){
8676                 this.select(this.selectedIndex-1);
8677             }
8678         }
8679     },
8680
8681     // private
8682     onKeyUp : function(e){
8683         if(this.editable !== false && !e.isSpecialKey()){
8684             this.lastKey = e.getKey();
8685             this.dqTask.delay(this.queryDelay);
8686         }
8687     },
8688
8689     // private
8690     validateBlur : function(){
8691         return !this.list || !this.list.isVisible();   
8692     },
8693
8694     // private
8695     initQuery : function(){
8696         this.doQuery(this.getRawValue());
8697     },
8698
8699     // private
8700     doForce : function(){
8701         if(this.el.dom.value.length > 0){
8702             this.el.dom.value =
8703                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8704              
8705         }
8706     },
8707
8708     /**
8709      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8710      * query allowing the query action to be canceled if needed.
8711      * @param {String} query The SQL query to execute
8712      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8713      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8714      * saved in the current store (defaults to false)
8715      */
8716     doQuery : function(q, forceAll){
8717         
8718         if(q === undefined || q === null){
8719             q = '';
8720         }
8721         var qe = {
8722             query: q,
8723             forceAll: forceAll,
8724             combo: this,
8725             cancel:false
8726         };
8727         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8728             return false;
8729         }
8730         q = qe.query;
8731         
8732         forceAll = qe.forceAll;
8733         if(forceAll === true || (q.length >= this.minChars)){
8734             
8735             this.hasQuery = true;
8736             
8737             if(this.lastQuery != q || this.alwaysQuery){
8738                 this.lastQuery = q;
8739                 if(this.mode == 'local'){
8740                     this.selectedIndex = -1;
8741                     if(forceAll){
8742                         this.store.clearFilter();
8743                     }else{
8744                         this.store.filter(this.displayField, q);
8745                     }
8746                     this.onLoad();
8747                 }else{
8748                     this.store.baseParams[this.queryParam] = q;
8749                     
8750                     var options = {params : this.getParams(q)};
8751                     
8752                     if(this.loadNext){
8753                         options.add = true;
8754                         options.params.start = this.page * this.pageSize;
8755                     }
8756                     
8757                     this.store.load(options);
8758                     this.expand();
8759                 }
8760             }else{
8761                 this.selectedIndex = -1;
8762                 this.onLoad();   
8763             }
8764         }
8765         
8766         this.loadNext = false;
8767     },
8768
8769     // private
8770     getParams : function(q){
8771         var p = {};
8772         //p[this.queryParam] = q;
8773         
8774         if(this.pageSize){
8775             p.start = 0;
8776             p.limit = this.pageSize;
8777         }
8778         return p;
8779     },
8780
8781     /**
8782      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8783      */
8784     collapse : function(){
8785         if(!this.isExpanded()){
8786             return;
8787         }
8788         
8789         this.list.hide();
8790         Roo.get(document).un('mousedown', this.collapseIf, this);
8791         Roo.get(document).un('mousewheel', this.collapseIf, this);
8792         if (!this.editable) {
8793             Roo.get(document).un('keydown', this.listKeyPress, this);
8794         }
8795         this.fireEvent('collapse', this);
8796     },
8797
8798     // private
8799     collapseIf : function(e){
8800         var in_combo  = e.within(this.el);
8801         var in_list =  e.within(this.list);
8802         
8803         if (in_combo || in_list) {
8804             //e.stopPropagation();
8805             return;
8806         }
8807
8808         this.collapse();
8809         
8810     },
8811
8812     /**
8813      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8814      */
8815     expand : function(){
8816        
8817         if(this.isExpanded() || !this.hasFocus){
8818             return;
8819         }
8820          Roo.log('expand');
8821         this.list.alignTo(this.inputEl(), this.listAlign);
8822         this.list.show();
8823         Roo.get(document).on('mousedown', this.collapseIf, this);
8824         Roo.get(document).on('mousewheel', this.collapseIf, this);
8825         if (!this.editable) {
8826             Roo.get(document).on('keydown', this.listKeyPress, this);
8827         }
8828         
8829         this.fireEvent('expand', this);
8830     },
8831
8832     // private
8833     // Implements the default empty TriggerField.onTriggerClick function
8834     onTriggerClick : function()
8835     {
8836         Roo.log('trigger click');
8837         
8838         if(this.disabled){
8839             return;
8840         }
8841         
8842         this.page = 0;
8843         this.loadNext = false;
8844         
8845         if(this.isExpanded()){
8846             this.collapse();
8847             if (!this.blockFocus) {
8848                 this.inputEl().focus();
8849             }
8850             
8851         }else {
8852             this.hasFocus = true;
8853             if(this.triggerAction == 'all') {
8854                 this.doQuery(this.allQuery, true);
8855             } else {
8856                 this.doQuery(this.getRawValue());
8857             }
8858             if (!this.blockFocus) {
8859                 this.inputEl().focus();
8860             }
8861         }
8862     },
8863     listKeyPress : function(e)
8864     {
8865         //Roo.log('listkeypress');
8866         // scroll to first matching element based on key pres..
8867         if (e.isSpecialKey()) {
8868             return false;
8869         }
8870         var k = String.fromCharCode(e.getKey()).toUpperCase();
8871         //Roo.log(k);
8872         var match  = false;
8873         var csel = this.view.getSelectedNodes();
8874         var cselitem = false;
8875         if (csel.length) {
8876             var ix = this.view.indexOf(csel[0]);
8877             cselitem  = this.store.getAt(ix);
8878             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8879                 cselitem = false;
8880             }
8881             
8882         }
8883         
8884         this.store.each(function(v) { 
8885             if (cselitem) {
8886                 // start at existing selection.
8887                 if (cselitem.id == v.id) {
8888                     cselitem = false;
8889                 }
8890                 return true;
8891             }
8892                 
8893             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8894                 match = this.store.indexOf(v);
8895                 return false;
8896             }
8897             return true;
8898         }, this);
8899         
8900         if (match === false) {
8901             return true; // no more action?
8902         }
8903         // scroll to?
8904         this.view.select(match);
8905         var sn = Roo.get(this.view.getSelectedNodes()[0])
8906         //sn.scrollIntoView(sn.dom.parentNode, false);
8907     },
8908     
8909     onViewScroll : function(e, t){
8910         
8911         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8912             return;
8913         }
8914         
8915         this.hasQuery = true;
8916         
8917         this.loading = this.list.select('.loading', true).first();
8918         
8919         if(this.loading === null){
8920             this.list.createChild({
8921                 tag: 'div',
8922                 cls: 'loading select2-more-results select2-active',
8923                 html: 'Loading more results...'
8924             })
8925             
8926             this.loading = this.list.select('.loading', true).first();
8927             
8928             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8929             
8930             this.loading.hide();
8931         }
8932         
8933         this.loading.show();
8934         
8935         var _combo = this;
8936         
8937         this.page++;
8938         this.loadNext = true;
8939         
8940         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8941         
8942         return;
8943     },
8944     
8945     addItem : function(o)
8946     {   
8947         var dv = ''; // display value
8948         
8949         if (this.displayField) {
8950             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8951         } else {
8952             // this is an error condition!!!
8953             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8954         }
8955         
8956         if(!dv.length){
8957             return;
8958         }
8959         
8960         var choice = this.choices.createChild({
8961             tag: 'li',
8962             cls: 'select2-search-choice',
8963             cn: [
8964                 {
8965                     tag: 'div',
8966                     html: dv
8967                 },
8968                 {
8969                     tag: 'a',
8970                     href: '#',
8971                     cls: 'select2-search-choice-close',
8972                     tabindex: '-1'
8973                 }
8974             ]
8975             
8976         }, this.searchField);
8977         
8978         var close = choice.select('a.select2-search-choice-close', true).first()
8979         
8980         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8981         
8982         this.item.push(o);
8983         this.lastData = o;
8984         
8985         this.syncValue();
8986         
8987         this.inputEl().dom.value = '';
8988         
8989     },
8990     
8991     onRemoveItem : function(e, _self, o)
8992     {
8993         Roo.log('remove item');
8994         var index = this.item.indexOf(o.data) * 1;
8995         
8996         if( index < 0){
8997             Roo.log('not this item?!');
8998             return;
8999         }
9000         
9001         this.item.splice(index, 1);
9002         o.item.remove();
9003         
9004         this.syncValue();
9005         
9006         this.fireEvent('remove', this);
9007         
9008     },
9009     
9010     syncValue : function()
9011     {
9012         if(!this.item.length){
9013             this.clearValue();
9014             return;
9015         }
9016             
9017         var value = [];
9018         var _this = this;
9019         Roo.each(this.item, function(i){
9020             if(_this.valueField){
9021                 value.push(i[_this.valueField]);
9022                 return;
9023             }
9024
9025             value.push(i);
9026         });
9027
9028         this.value = value.join(',');
9029
9030         if(this.hiddenField){
9031             this.hiddenField.dom.value = this.value;
9032         }
9033     },
9034     
9035     clearItem : function()
9036     {
9037         if(!this.multiple){
9038             return;
9039         }
9040         
9041         this.item = [];
9042         
9043         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9044            c.remove();
9045         });
9046         
9047         this.syncValue();
9048     }
9049     
9050     
9051
9052     /** 
9053     * @cfg {Boolean} grow 
9054     * @hide 
9055     */
9056     /** 
9057     * @cfg {Number} growMin 
9058     * @hide 
9059     */
9060     /** 
9061     * @cfg {Number} growMax 
9062     * @hide 
9063     */
9064     /**
9065      * @hide
9066      * @method autoSize
9067      */
9068 });
9069 /*
9070  * Based on:
9071  * Ext JS Library 1.1.1
9072  * Copyright(c) 2006-2007, Ext JS, LLC.
9073  *
9074  * Originally Released Under LGPL - original licence link has changed is not relivant.
9075  *
9076  * Fork - LGPL
9077  * <script type="text/javascript">
9078  */
9079
9080 /**
9081  * @class Roo.View
9082  * @extends Roo.util.Observable
9083  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9084  * This class also supports single and multi selection modes. <br>
9085  * Create a data model bound view:
9086  <pre><code>
9087  var store = new Roo.data.Store(...);
9088
9089  var view = new Roo.View({
9090     el : "my-element",
9091     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9092  
9093     singleSelect: true,
9094     selectedClass: "ydataview-selected",
9095     store: store
9096  });
9097
9098  // listen for node click?
9099  view.on("click", function(vw, index, node, e){
9100  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9101  });
9102
9103  // load XML data
9104  dataModel.load("foobar.xml");
9105  </code></pre>
9106  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9107  * <br><br>
9108  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9109  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9110  * 
9111  * Note: old style constructor is still suported (container, template, config)
9112  * 
9113  * @constructor
9114  * Create a new View
9115  * @param {Object} config The config object
9116  * 
9117  */
9118 Roo.View = function(config, depreciated_tpl, depreciated_config){
9119     
9120     if (typeof(depreciated_tpl) == 'undefined') {
9121         // new way.. - universal constructor.
9122         Roo.apply(this, config);
9123         this.el  = Roo.get(this.el);
9124     } else {
9125         // old format..
9126         this.el  = Roo.get(config);
9127         this.tpl = depreciated_tpl;
9128         Roo.apply(this, depreciated_config);
9129     }
9130     this.wrapEl  = this.el.wrap().wrap();
9131     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9132     
9133     
9134     if(typeof(this.tpl) == "string"){
9135         this.tpl = new Roo.Template(this.tpl);
9136     } else {
9137         // support xtype ctors..
9138         this.tpl = new Roo.factory(this.tpl, Roo);
9139     }
9140     
9141     
9142     this.tpl.compile();
9143    
9144   
9145     
9146      
9147     /** @private */
9148     this.addEvents({
9149         /**
9150          * @event beforeclick
9151          * Fires before a click is processed. Returns false to cancel the default action.
9152          * @param {Roo.View} this
9153          * @param {Number} index The index of the target node
9154          * @param {HTMLElement} node The target node
9155          * @param {Roo.EventObject} e The raw event object
9156          */
9157             "beforeclick" : true,
9158         /**
9159          * @event click
9160          * Fires when a template node is clicked.
9161          * @param {Roo.View} this
9162          * @param {Number} index The index of the target node
9163          * @param {HTMLElement} node The target node
9164          * @param {Roo.EventObject} e The raw event object
9165          */
9166             "click" : true,
9167         /**
9168          * @event dblclick
9169          * Fires when a template node is double clicked.
9170          * @param {Roo.View} this
9171          * @param {Number} index The index of the target node
9172          * @param {HTMLElement} node The target node
9173          * @param {Roo.EventObject} e The raw event object
9174          */
9175             "dblclick" : true,
9176         /**
9177          * @event contextmenu
9178          * Fires when a template node is right clicked.
9179          * @param {Roo.View} this
9180          * @param {Number} index The index of the target node
9181          * @param {HTMLElement} node The target node
9182          * @param {Roo.EventObject} e The raw event object
9183          */
9184             "contextmenu" : true,
9185         /**
9186          * @event selectionchange
9187          * Fires when the selected nodes change.
9188          * @param {Roo.View} this
9189          * @param {Array} selections Array of the selected nodes
9190          */
9191             "selectionchange" : true,
9192     
9193         /**
9194          * @event beforeselect
9195          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9196          * @param {Roo.View} this
9197          * @param {HTMLElement} node The node to be selected
9198          * @param {Array} selections Array of currently selected nodes
9199          */
9200             "beforeselect" : true,
9201         /**
9202          * @event preparedata
9203          * Fires on every row to render, to allow you to change the data.
9204          * @param {Roo.View} this
9205          * @param {Object} data to be rendered (change this)
9206          */
9207           "preparedata" : true
9208           
9209           
9210         });
9211
9212
9213
9214     this.el.on({
9215         "click": this.onClick,
9216         "dblclick": this.onDblClick,
9217         "contextmenu": this.onContextMenu,
9218         scope:this
9219     });
9220
9221     this.selections = [];
9222     this.nodes = [];
9223     this.cmp = new Roo.CompositeElementLite([]);
9224     if(this.store){
9225         this.store = Roo.factory(this.store, Roo.data);
9226         this.setStore(this.store, true);
9227     }
9228     
9229     if ( this.footer && this.footer.xtype) {
9230            
9231          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9232         
9233         this.footer.dataSource = this.store
9234         this.footer.container = fctr;
9235         this.footer = Roo.factory(this.footer, Roo);
9236         fctr.insertFirst(this.el);
9237         
9238         // this is a bit insane - as the paging toolbar seems to detach the el..
9239 //        dom.parentNode.parentNode.parentNode
9240          // they get detached?
9241     }
9242     
9243     
9244     Roo.View.superclass.constructor.call(this);
9245     
9246     
9247 };
9248
9249 Roo.extend(Roo.View, Roo.util.Observable, {
9250     
9251      /**
9252      * @cfg {Roo.data.Store} store Data store to load data from.
9253      */
9254     store : false,
9255     
9256     /**
9257      * @cfg {String|Roo.Element} el The container element.
9258      */
9259     el : '',
9260     
9261     /**
9262      * @cfg {String|Roo.Template} tpl The template used by this View 
9263      */
9264     tpl : false,
9265     /**
9266      * @cfg {String} dataName the named area of the template to use as the data area
9267      *                          Works with domtemplates roo-name="name"
9268      */
9269     dataName: false,
9270     /**
9271      * @cfg {String} selectedClass The css class to add to selected nodes
9272      */
9273     selectedClass : "x-view-selected",
9274      /**
9275      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9276      */
9277     emptyText : "",
9278     
9279     /**
9280      * @cfg {String} text to display on mask (default Loading)
9281      */
9282     mask : false,
9283     /**
9284      * @cfg {Boolean} multiSelect Allow multiple selection
9285      */
9286     multiSelect : false,
9287     /**
9288      * @cfg {Boolean} singleSelect Allow single selection
9289      */
9290     singleSelect:  false,
9291     
9292     /**
9293      * @cfg {Boolean} toggleSelect - selecting 
9294      */
9295     toggleSelect : false,
9296     
9297     /**
9298      * Returns the element this view is bound to.
9299      * @return {Roo.Element}
9300      */
9301     getEl : function(){
9302         return this.wrapEl;
9303     },
9304     
9305     
9306
9307     /**
9308      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9309      */
9310     refresh : function(){
9311         Roo.log('refresh');
9312         var t = this.tpl;
9313         
9314         // if we are using something like 'domtemplate', then
9315         // the what gets used is:
9316         // t.applySubtemplate(NAME, data, wrapping data..)
9317         // the outer template then get' applied with
9318         //     the store 'extra data'
9319         // and the body get's added to the
9320         //      roo-name="data" node?
9321         //      <span class='roo-tpl-{name}'></span> ?????
9322         
9323         
9324         
9325         this.clearSelections();
9326         this.el.update("");
9327         var html = [];
9328         var records = this.store.getRange();
9329         if(records.length < 1) {
9330             
9331             // is this valid??  = should it render a template??
9332             
9333             this.el.update(this.emptyText);
9334             return;
9335         }
9336         var el = this.el;
9337         if (this.dataName) {
9338             this.el.update(t.apply(this.store.meta)); //????
9339             el = this.el.child('.roo-tpl-' + this.dataName);
9340         }
9341         
9342         for(var i = 0, len = records.length; i < len; i++){
9343             var data = this.prepareData(records[i].data, i, records[i]);
9344             this.fireEvent("preparedata", this, data, i, records[i]);
9345             html[html.length] = Roo.util.Format.trim(
9346                 this.dataName ?
9347                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9348                     t.apply(data)
9349             );
9350         }
9351         
9352         
9353         
9354         el.update(html.join(""));
9355         this.nodes = el.dom.childNodes;
9356         this.updateIndexes(0);
9357     },
9358     
9359
9360     /**
9361      * Function to override to reformat the data that is sent to
9362      * the template for each node.
9363      * DEPRICATED - use the preparedata event handler.
9364      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9365      * a JSON object for an UpdateManager bound view).
9366      */
9367     prepareData : function(data, index, record)
9368     {
9369         this.fireEvent("preparedata", this, data, index, record);
9370         return data;
9371     },
9372
9373     onUpdate : function(ds, record){
9374          Roo.log('on update');   
9375         this.clearSelections();
9376         var index = this.store.indexOf(record);
9377         var n = this.nodes[index];
9378         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9379         n.parentNode.removeChild(n);
9380         this.updateIndexes(index, index);
9381     },
9382
9383     
9384     
9385 // --------- FIXME     
9386     onAdd : function(ds, records, index)
9387     {
9388         Roo.log(['on Add', ds, records, index] );        
9389         this.clearSelections();
9390         if(this.nodes.length == 0){
9391             this.refresh();
9392             return;
9393         }
9394         var n = this.nodes[index];
9395         for(var i = 0, len = records.length; i < len; i++){
9396             var d = this.prepareData(records[i].data, i, records[i]);
9397             if(n){
9398                 this.tpl.insertBefore(n, d);
9399             }else{
9400                 
9401                 this.tpl.append(this.el, d);
9402             }
9403         }
9404         this.updateIndexes(index);
9405     },
9406
9407     onRemove : function(ds, record, index){
9408         Roo.log('onRemove');
9409         this.clearSelections();
9410         var el = this.dataName  ?
9411             this.el.child('.roo-tpl-' + this.dataName) :
9412             this.el; 
9413         
9414         el.dom.removeChild(this.nodes[index]);
9415         this.updateIndexes(index);
9416     },
9417
9418     /**
9419      * Refresh an individual node.
9420      * @param {Number} index
9421      */
9422     refreshNode : function(index){
9423         this.onUpdate(this.store, this.store.getAt(index));
9424     },
9425
9426     updateIndexes : function(startIndex, endIndex){
9427         var ns = this.nodes;
9428         startIndex = startIndex || 0;
9429         endIndex = endIndex || ns.length - 1;
9430         for(var i = startIndex; i <= endIndex; i++){
9431             ns[i].nodeIndex = i;
9432         }
9433     },
9434
9435     /**
9436      * Changes the data store this view uses and refresh the view.
9437      * @param {Store} store
9438      */
9439     setStore : function(store, initial){
9440         if(!initial && this.store){
9441             this.store.un("datachanged", this.refresh);
9442             this.store.un("add", this.onAdd);
9443             this.store.un("remove", this.onRemove);
9444             this.store.un("update", this.onUpdate);
9445             this.store.un("clear", this.refresh);
9446             this.store.un("beforeload", this.onBeforeLoad);
9447             this.store.un("load", this.onLoad);
9448             this.store.un("loadexception", this.onLoad);
9449         }
9450         if(store){
9451           
9452             store.on("datachanged", this.refresh, this);
9453             store.on("add", this.onAdd, this);
9454             store.on("remove", this.onRemove, this);
9455             store.on("update", this.onUpdate, this);
9456             store.on("clear", this.refresh, this);
9457             store.on("beforeload", this.onBeforeLoad, this);
9458             store.on("load", this.onLoad, this);
9459             store.on("loadexception", this.onLoad, this);
9460         }
9461         
9462         if(store){
9463             this.refresh();
9464         }
9465     },
9466     /**
9467      * onbeforeLoad - masks the loading area.
9468      *
9469      */
9470     onBeforeLoad : function(store,opts)
9471     {
9472          Roo.log('onBeforeLoad');   
9473         if (!opts.add) {
9474             this.el.update("");
9475         }
9476         this.el.mask(this.mask ? this.mask : "Loading" ); 
9477     },
9478     onLoad : function ()
9479     {
9480         this.el.unmask();
9481     },
9482     
9483
9484     /**
9485      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9486      * @param {HTMLElement} node
9487      * @return {HTMLElement} The template node
9488      */
9489     findItemFromChild : function(node){
9490         var el = this.dataName  ?
9491             this.el.child('.roo-tpl-' + this.dataName,true) :
9492             this.el.dom; 
9493         
9494         if(!node || node.parentNode == el){
9495                     return node;
9496             }
9497             var p = node.parentNode;
9498             while(p && p != el){
9499             if(p.parentNode == el){
9500                 return p;
9501             }
9502             p = p.parentNode;
9503         }
9504             return null;
9505     },
9506
9507     /** @ignore */
9508     onClick : function(e){
9509         var item = this.findItemFromChild(e.getTarget());
9510         if(item){
9511             var index = this.indexOf(item);
9512             if(this.onItemClick(item, index, e) !== false){
9513                 this.fireEvent("click", this, index, item, e);
9514             }
9515         }else{
9516             this.clearSelections();
9517         }
9518     },
9519
9520     /** @ignore */
9521     onContextMenu : function(e){
9522         var item = this.findItemFromChild(e.getTarget());
9523         if(item){
9524             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9525         }
9526     },
9527
9528     /** @ignore */
9529     onDblClick : function(e){
9530         var item = this.findItemFromChild(e.getTarget());
9531         if(item){
9532             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9533         }
9534     },
9535
9536     onItemClick : function(item, index, e)
9537     {
9538         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9539             return false;
9540         }
9541         if (this.toggleSelect) {
9542             var m = this.isSelected(item) ? 'unselect' : 'select';
9543             Roo.log(m);
9544             var _t = this;
9545             _t[m](item, true, false);
9546             return true;
9547         }
9548         if(this.multiSelect || this.singleSelect){
9549             if(this.multiSelect && e.shiftKey && this.lastSelection){
9550                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9551             }else{
9552                 this.select(item, this.multiSelect && e.ctrlKey);
9553                 this.lastSelection = item;
9554             }
9555             e.preventDefault();
9556         }
9557         return true;
9558     },
9559
9560     /**
9561      * Get the number of selected nodes.
9562      * @return {Number}
9563      */
9564     getSelectionCount : function(){
9565         return this.selections.length;
9566     },
9567
9568     /**
9569      * Get the currently selected nodes.
9570      * @return {Array} An array of HTMLElements
9571      */
9572     getSelectedNodes : function(){
9573         return this.selections;
9574     },
9575
9576     /**
9577      * Get the indexes of the selected nodes.
9578      * @return {Array}
9579      */
9580     getSelectedIndexes : function(){
9581         var indexes = [], s = this.selections;
9582         for(var i = 0, len = s.length; i < len; i++){
9583             indexes.push(s[i].nodeIndex);
9584         }
9585         return indexes;
9586     },
9587
9588     /**
9589      * Clear all selections
9590      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9591      */
9592     clearSelections : function(suppressEvent){
9593         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9594             this.cmp.elements = this.selections;
9595             this.cmp.removeClass(this.selectedClass);
9596             this.selections = [];
9597             if(!suppressEvent){
9598                 this.fireEvent("selectionchange", this, this.selections);
9599             }
9600         }
9601     },
9602
9603     /**
9604      * Returns true if the passed node is selected
9605      * @param {HTMLElement/Number} node The node or node index
9606      * @return {Boolean}
9607      */
9608     isSelected : function(node){
9609         var s = this.selections;
9610         if(s.length < 1){
9611             return false;
9612         }
9613         node = this.getNode(node);
9614         return s.indexOf(node) !== -1;
9615     },
9616
9617     /**
9618      * Selects nodes.
9619      * @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
9620      * @param {Boolean} keepExisting (optional) true to keep existing selections
9621      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9622      */
9623     select : function(nodeInfo, keepExisting, suppressEvent){
9624         if(nodeInfo instanceof Array){
9625             if(!keepExisting){
9626                 this.clearSelections(true);
9627             }
9628             for(var i = 0, len = nodeInfo.length; i < len; i++){
9629                 this.select(nodeInfo[i], true, true);
9630             }
9631             return;
9632         } 
9633         var node = this.getNode(nodeInfo);
9634         if(!node || this.isSelected(node)){
9635             return; // already selected.
9636         }
9637         if(!keepExisting){
9638             this.clearSelections(true);
9639         }
9640         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9641             Roo.fly(node).addClass(this.selectedClass);
9642             this.selections.push(node);
9643             if(!suppressEvent){
9644                 this.fireEvent("selectionchange", this, this.selections);
9645             }
9646         }
9647         
9648         
9649     },
9650       /**
9651      * Unselects nodes.
9652      * @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
9653      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9654      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9655      */
9656     unselect : function(nodeInfo, keepExisting, suppressEvent)
9657     {
9658         if(nodeInfo instanceof Array){
9659             Roo.each(this.selections, function(s) {
9660                 this.unselect(s, nodeInfo);
9661             }, this);
9662             return;
9663         }
9664         var node = this.getNode(nodeInfo);
9665         if(!node || !this.isSelected(node)){
9666             Roo.log("not selected");
9667             return; // not selected.
9668         }
9669         // fireevent???
9670         var ns = [];
9671         Roo.each(this.selections, function(s) {
9672             if (s == node ) {
9673                 Roo.fly(node).removeClass(this.selectedClass);
9674
9675                 return;
9676             }
9677             ns.push(s);
9678         },this);
9679         
9680         this.selections= ns;
9681         this.fireEvent("selectionchange", this, this.selections);
9682     },
9683
9684     /**
9685      * Gets a template node.
9686      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9687      * @return {HTMLElement} The node or null if it wasn't found
9688      */
9689     getNode : function(nodeInfo){
9690         if(typeof nodeInfo == "string"){
9691             return document.getElementById(nodeInfo);
9692         }else if(typeof nodeInfo == "number"){
9693             return this.nodes[nodeInfo];
9694         }
9695         return nodeInfo;
9696     },
9697
9698     /**
9699      * Gets a range template nodes.
9700      * @param {Number} startIndex
9701      * @param {Number} endIndex
9702      * @return {Array} An array of nodes
9703      */
9704     getNodes : function(start, end){
9705         var ns = this.nodes;
9706         start = start || 0;
9707         end = typeof end == "undefined" ? ns.length - 1 : end;
9708         var nodes = [];
9709         if(start <= end){
9710             for(var i = start; i <= end; i++){
9711                 nodes.push(ns[i]);
9712             }
9713         } else{
9714             for(var i = start; i >= end; i--){
9715                 nodes.push(ns[i]);
9716             }
9717         }
9718         return nodes;
9719     },
9720
9721     /**
9722      * Finds the index of the passed node
9723      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9724      * @return {Number} The index of the node or -1
9725      */
9726     indexOf : function(node){
9727         node = this.getNode(node);
9728         if(typeof node.nodeIndex == "number"){
9729             return node.nodeIndex;
9730         }
9731         var ns = this.nodes;
9732         for(var i = 0, len = ns.length; i < len; i++){
9733             if(ns[i] == node){
9734                 return i;
9735             }
9736         }
9737         return -1;
9738     }
9739 });
9740 /*
9741  * - LGPL
9742  *
9743  * based on jquery fullcalendar
9744  * 
9745  */
9746
9747 Roo.bootstrap = Roo.bootstrap || {};
9748 /**
9749  * @class Roo.bootstrap.Calendar
9750  * @extends Roo.bootstrap.Component
9751  * Bootstrap Calendar class
9752  * @cfg {Boolean} loadMask (true|false) default false
9753     
9754  * @constructor
9755  * Create a new Container
9756  * @param {Object} config The config object
9757  */
9758
9759
9760
9761 Roo.bootstrap.Calendar = function(config){
9762     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9763      this.addEvents({
9764         /**
9765              * @event select
9766              * Fires when a date is selected
9767              * @param {DatePicker} this
9768              * @param {Date} date The selected date
9769              */
9770         'select': true,
9771         /**
9772              * @event monthchange
9773              * Fires when the displayed month changes 
9774              * @param {DatePicker} this
9775              * @param {Date} date The selected month
9776              */
9777         'monthchange': true,
9778         /**
9779              * @event evententer
9780              * Fires when mouse over an event
9781              * @param {Calendar} this
9782              * @param {event} Event
9783              */
9784         'evententer': true,
9785         /**
9786              * @event eventleave
9787              * Fires when the mouse leaves an
9788              * @param {Calendar} this
9789              * @param {event}
9790              */
9791         'eventleave': true,
9792         /**
9793              * @event eventclick
9794              * Fires when the mouse click an
9795              * @param {Calendar} this
9796              * @param {event}
9797              */
9798         'eventclick': true
9799         
9800     });
9801
9802 };
9803
9804 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9805     
9806      /**
9807      * @cfg {Number} startDay
9808      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9809      */
9810     startDay : 0,
9811     
9812     loadMask : false,
9813       
9814     getAutoCreate : function(){
9815         
9816         
9817         var fc_button = function(name, corner, style, content ) {
9818             return Roo.apply({},{
9819                 tag : 'span',
9820                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9821                          (corner.length ?
9822                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9823                             ''
9824                         ),
9825                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9826                 unselectable: 'on'
9827             });
9828         };
9829         
9830         var header = {
9831             tag : 'table',
9832             cls : 'fc-header',
9833             style : 'width:100%',
9834             cn : [
9835                 {
9836                     tag: 'tr',
9837                     cn : [
9838                         {
9839                             tag : 'td',
9840                             cls : 'fc-header-left',
9841                             cn : [
9842                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9843                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9844                                 { tag: 'span', cls: 'fc-header-space' },
9845                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9846                                 
9847                                 
9848                             ]
9849                         },
9850                         
9851                         {
9852                             tag : 'td',
9853                             cls : 'fc-header-center',
9854                             cn : [
9855                                 {
9856                                     tag: 'span',
9857                                     cls: 'fc-header-title',
9858                                     cn : {
9859                                         tag: 'H2',
9860                                         html : 'month / year'
9861                                     }
9862                                 }
9863                                 
9864                             ]
9865                         },
9866                         {
9867                             tag : 'td',
9868                             cls : 'fc-header-right',
9869                             cn : [
9870                           /*      fc_button('month', 'left', '', 'month' ),
9871                                 fc_button('week', '', '', 'week' ),
9872                                 fc_button('day', 'right', '', 'day' )
9873                             */    
9874                                 
9875                             ]
9876                         }
9877                         
9878                     ]
9879                 }
9880             ]
9881         };
9882         
9883        
9884         var cal_heads = function() {
9885             var ret = [];
9886             // fixme - handle this.
9887             
9888             for (var i =0; i < Date.dayNames.length; i++) {
9889                 var d = Date.dayNames[i];
9890                 ret.push({
9891                     tag: 'th',
9892                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9893                     html : d.substring(0,3)
9894                 });
9895                 
9896             }
9897             ret[0].cls += ' fc-first';
9898             ret[6].cls += ' fc-last';
9899             return ret;
9900         };
9901         var cal_cell = function(n) {
9902             return  {
9903                 tag: 'td',
9904                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9905                 cn : [
9906                     {
9907                         cn : [
9908                             {
9909                                 cls: 'fc-day-number',
9910                                 html: 'D'
9911                             },
9912                             {
9913                                 cls: 'fc-day-content',
9914                              
9915                                 cn : [
9916                                      {
9917                                         style: 'position: relative;' // height: 17px;
9918                                     }
9919                                 ]
9920                             }
9921                             
9922                             
9923                         ]
9924                     }
9925                 ]
9926                 
9927             }
9928         };
9929         var cal_rows = function() {
9930             
9931             var ret = []
9932             for (var r = 0; r < 6; r++) {
9933                 var row= {
9934                     tag : 'tr',
9935                     cls : 'fc-week',
9936                     cn : []
9937                 };
9938                 
9939                 for (var i =0; i < Date.dayNames.length; i++) {
9940                     var d = Date.dayNames[i];
9941                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9942
9943                 }
9944                 row.cn[0].cls+=' fc-first';
9945                 row.cn[0].cn[0].style = 'min-height:90px';
9946                 row.cn[6].cls+=' fc-last';
9947                 ret.push(row);
9948                 
9949             }
9950             ret[0].cls += ' fc-first';
9951             ret[4].cls += ' fc-prev-last';
9952             ret[5].cls += ' fc-last';
9953             return ret;
9954             
9955         };
9956         
9957         var cal_table = {
9958             tag: 'table',
9959             cls: 'fc-border-separate',
9960             style : 'width:100%',
9961             cellspacing  : 0,
9962             cn : [
9963                 { 
9964                     tag: 'thead',
9965                     cn : [
9966                         { 
9967                             tag: 'tr',
9968                             cls : 'fc-first fc-last',
9969                             cn : cal_heads()
9970                         }
9971                     ]
9972                 },
9973                 { 
9974                     tag: 'tbody',
9975                     cn : cal_rows()
9976                 }
9977                   
9978             ]
9979         };
9980          
9981          var cfg = {
9982             cls : 'fc fc-ltr',
9983             cn : [
9984                 header,
9985                 {
9986                     cls : 'fc-content',
9987                     style : "position: relative;",
9988                     cn : [
9989                         {
9990                             cls : 'fc-view fc-view-month fc-grid',
9991                             style : 'position: relative',
9992                             unselectable : 'on',
9993                             cn : [
9994                                 {
9995                                     cls : 'fc-event-container',
9996                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9997                                 },
9998                                 cal_table
9999                             ]
10000                         }
10001                     ]
10002     
10003                 }
10004            ] 
10005             
10006         };
10007         
10008          
10009         
10010         return cfg;
10011     },
10012     
10013     
10014     initEvents : function()
10015     {
10016         if(!this.store){
10017             throw "can not find store for calendar";
10018         }
10019         
10020         var mark = {
10021             tag: "div",
10022             cls:"x-dlg-mask",
10023             style: "text-align:center",
10024             cn: [
10025                 {
10026                     tag: "div",
10027                     style: "background-color:white;width:50%;margin:250 auto",
10028                     cn: [
10029                         {
10030                             tag: "img",
10031                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10032                         },
10033                         {
10034                             tag: "span",
10035                             html: "Loading"
10036                         }
10037                         
10038                     ]
10039                 }
10040             ]
10041         }
10042         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10043         
10044         var size = this.el.select('.fc-content', true).first().getSize();
10045         this.maskEl.setSize(size.width, size.height);
10046         this.maskEl.enableDisplayMode("block");
10047         if(!this.loadMask){
10048             this.maskEl.hide();
10049         }
10050         
10051         this.store = Roo.factory(this.store, Roo.data);
10052         this.store.on('load', this.onLoad, this);
10053         this.store.on('beforeload', this.onBeforeLoad, this);
10054         
10055         this.resize();
10056         
10057         this.cells = this.el.select('.fc-day',true);
10058         //Roo.log(this.cells);
10059         this.textNodes = this.el.query('.fc-day-number');
10060         this.cells.addClassOnOver('fc-state-hover');
10061         
10062         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10063         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10064         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10065         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10066         
10067         this.on('monthchange', this.onMonthChange, this);
10068         
10069         this.update(new Date().clearTime());
10070     },
10071     
10072     resize : function() {
10073         var sz  = this.el.getSize();
10074         
10075         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10076         this.el.select('.fc-day-content div',true).setHeight(34);
10077     },
10078     
10079     
10080     // private
10081     showPrevMonth : function(e){
10082         this.update(this.activeDate.add("mo", -1));
10083     },
10084     showToday : function(e){
10085         this.update(new Date().clearTime());
10086     },
10087     // private
10088     showNextMonth : function(e){
10089         this.update(this.activeDate.add("mo", 1));
10090     },
10091
10092     // private
10093     showPrevYear : function(){
10094         this.update(this.activeDate.add("y", -1));
10095     },
10096
10097     // private
10098     showNextYear : function(){
10099         this.update(this.activeDate.add("y", 1));
10100     },
10101
10102     
10103    // private
10104     update : function(date)
10105     {
10106         var vd = this.activeDate;
10107         this.activeDate = date;
10108 //        if(vd && this.el){
10109 //            var t = date.getTime();
10110 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10111 //                Roo.log('using add remove');
10112 //                
10113 //                this.fireEvent('monthchange', this, date);
10114 //                
10115 //                this.cells.removeClass("fc-state-highlight");
10116 //                this.cells.each(function(c){
10117 //                   if(c.dateValue == t){
10118 //                       c.addClass("fc-state-highlight");
10119 //                       setTimeout(function(){
10120 //                            try{c.dom.firstChild.focus();}catch(e){}
10121 //                       }, 50);
10122 //                       return false;
10123 //                   }
10124 //                   return true;
10125 //                });
10126 //                return;
10127 //            }
10128 //        }
10129         
10130         var days = date.getDaysInMonth();
10131         
10132         var firstOfMonth = date.getFirstDateOfMonth();
10133         var startingPos = firstOfMonth.getDay()-this.startDay;
10134         
10135         if(startingPos < this.startDay){
10136             startingPos += 7;
10137         }
10138         
10139         var pm = date.add(Date.MONTH, -1);
10140         var prevStart = pm.getDaysInMonth()-startingPos;
10141 //        
10142         this.cells = this.el.select('.fc-day',true);
10143         this.textNodes = this.el.query('.fc-day-number');
10144         this.cells.addClassOnOver('fc-state-hover');
10145         
10146         var cells = this.cells.elements;
10147         var textEls = this.textNodes;
10148         
10149         Roo.each(cells, function(cell){
10150             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10151         });
10152         
10153         days += startingPos;
10154
10155         // convert everything to numbers so it's fast
10156         var day = 86400000;
10157         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10158         //Roo.log(d);
10159         //Roo.log(pm);
10160         //Roo.log(prevStart);
10161         
10162         var today = new Date().clearTime().getTime();
10163         var sel = date.clearTime().getTime();
10164         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10165         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10166         var ddMatch = this.disabledDatesRE;
10167         var ddText = this.disabledDatesText;
10168         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10169         var ddaysText = this.disabledDaysText;
10170         var format = this.format;
10171         
10172         var setCellClass = function(cal, cell){
10173             
10174             //Roo.log('set Cell Class');
10175             cell.title = "";
10176             var t = d.getTime();
10177             
10178             //Roo.log(d);
10179             
10180             cell.dateValue = t;
10181             if(t == today){
10182                 cell.className += " fc-today";
10183                 cell.className += " fc-state-highlight";
10184                 cell.title = cal.todayText;
10185             }
10186             if(t == sel){
10187                 // disable highlight in other month..
10188                 //cell.className += " fc-state-highlight";
10189                 
10190             }
10191             // disabling
10192             if(t < min) {
10193                 cell.className = " fc-state-disabled";
10194                 cell.title = cal.minText;
10195                 return;
10196             }
10197             if(t > max) {
10198                 cell.className = " fc-state-disabled";
10199                 cell.title = cal.maxText;
10200                 return;
10201             }
10202             if(ddays){
10203                 if(ddays.indexOf(d.getDay()) != -1){
10204                     cell.title = ddaysText;
10205                     cell.className = " fc-state-disabled";
10206                 }
10207             }
10208             if(ddMatch && format){
10209                 var fvalue = d.dateFormat(format);
10210                 if(ddMatch.test(fvalue)){
10211                     cell.title = ddText.replace("%0", fvalue);
10212                     cell.className = " fc-state-disabled";
10213                 }
10214             }
10215             
10216             if (!cell.initialClassName) {
10217                 cell.initialClassName = cell.dom.className;
10218             }
10219             
10220             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10221         };
10222
10223         var i = 0;
10224         
10225         for(; i < startingPos; i++) {
10226             textEls[i].innerHTML = (++prevStart);
10227             d.setDate(d.getDate()+1);
10228             
10229             cells[i].className = "fc-past fc-other-month";
10230             setCellClass(this, cells[i]);
10231         }
10232         
10233         var intDay = 0;
10234         
10235         for(; i < days; i++){
10236             intDay = i - startingPos + 1;
10237             textEls[i].innerHTML = (intDay);
10238             d.setDate(d.getDate()+1);
10239             
10240             cells[i].className = ''; // "x-date-active";
10241             setCellClass(this, cells[i]);
10242         }
10243         var extraDays = 0;
10244         
10245         for(; i < 42; i++) {
10246             textEls[i].innerHTML = (++extraDays);
10247             d.setDate(d.getDate()+1);
10248             
10249             cells[i].className = "fc-future fc-other-month";
10250             setCellClass(this, cells[i]);
10251         }
10252         
10253         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10254         
10255         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10256         
10257         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10258         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10259         
10260         if(totalRows != 6){
10261             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10262             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10263         }
10264         
10265         this.fireEvent('monthchange', this, date);
10266         
10267         
10268         /*
10269         if(!this.internalRender){
10270             var main = this.el.dom.firstChild;
10271             var w = main.offsetWidth;
10272             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10273             Roo.fly(main).setWidth(w);
10274             this.internalRender = true;
10275             // opera does not respect the auto grow header center column
10276             // then, after it gets a width opera refuses to recalculate
10277             // without a second pass
10278             if(Roo.isOpera && !this.secondPass){
10279                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10280                 this.secondPass = true;
10281                 this.update.defer(10, this, [date]);
10282             }
10283         }
10284         */
10285         
10286     },
10287     
10288     findCell : function(dt) {
10289         dt = dt.clearTime().getTime();
10290         var ret = false;
10291         this.cells.each(function(c){
10292             //Roo.log("check " +c.dateValue + '?=' + dt);
10293             if(c.dateValue == dt){
10294                 ret = c;
10295                 return false;
10296             }
10297             return true;
10298         });
10299         
10300         return ret;
10301     },
10302     
10303     findCells : function(ev) {
10304         var s = ev.start.clone().clearTime().getTime();
10305        // Roo.log(s);
10306         var e= ev.end.clone().clearTime().getTime();
10307        // Roo.log(e);
10308         var ret = [];
10309         this.cells.each(function(c){
10310              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10311             
10312             if(c.dateValue > e){
10313                 return ;
10314             }
10315             if(c.dateValue < s){
10316                 return ;
10317             }
10318             ret.push(c);
10319         });
10320         
10321         return ret;    
10322     },
10323     
10324     findBestRow: function(cells)
10325     {
10326         var ret = 0;
10327         
10328         for (var i =0 ; i < cells.length;i++) {
10329             ret  = Math.max(cells[i].rows || 0,ret);
10330         }
10331         return ret;
10332         
10333     },
10334     
10335     
10336     addItem : function(ev)
10337     {
10338         // look for vertical location slot in
10339         var cells = this.findCells(ev);
10340         
10341         ev.row = this.findBestRow(cells);
10342         
10343         // work out the location.
10344         
10345         var crow = false;
10346         var rows = [];
10347         for(var i =0; i < cells.length; i++) {
10348             if (!crow) {
10349                 crow = {
10350                     start : cells[i],
10351                     end :  cells[i]
10352                 };
10353                 continue;
10354             }
10355             if (crow.start.getY() == cells[i].getY()) {
10356                 // on same row.
10357                 crow.end = cells[i];
10358                 continue;
10359             }
10360             // different row.
10361             rows.push(crow);
10362             crow = {
10363                 start: cells[i],
10364                 end : cells[i]
10365             };
10366             
10367         }
10368         
10369         rows.push(crow);
10370         ev.els = [];
10371         ev.rows = rows;
10372         ev.cells = cells;
10373         for (var i = 0; i < cells.length;i++) {
10374             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10375             
10376         }
10377         
10378         this.calevents.push(ev);
10379     },
10380     
10381     clearEvents: function() {
10382         
10383         if(!this.calevents){
10384             return;
10385         }
10386         
10387         Roo.each(this.cells.elements, function(c){
10388             c.rows = 0;
10389         });
10390         
10391         Roo.each(this.calevents, function(e) {
10392             Roo.each(e.els, function(el) {
10393                 el.un('mouseenter' ,this.onEventEnter, this);
10394                 el.un('mouseleave' ,this.onEventLeave, this);
10395                 el.remove();
10396             },this);
10397         },this);
10398         
10399     },
10400     
10401     renderEvents: function()
10402     {   
10403         // first make sure there is enough space..
10404         
10405         this.cells.each(function(c) {
10406         
10407             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10408         });
10409         
10410         for (var e = 0; e < this.calevents.length; e++) {
10411             var ev = this.calevents[e];
10412             var cells = ev.cells;
10413             var rows = ev.rows;
10414             
10415             for(var i =0; i < rows.length; i++) {
10416                 
10417                  
10418                 // how many rows should it span..
10419                 
10420                 var  cfg = {
10421                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10422                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10423                     
10424                     unselectable : "on",
10425                     cn : [
10426                         {
10427                             cls: 'fc-event-inner',
10428                             cn : [
10429 //                                {
10430 //                                  tag:'span',
10431 //                                  cls: 'fc-event-time',
10432 //                                  html : cells.length > 1 ? '' : ev.time
10433 //                                },
10434                                 {
10435                                   tag:'span',
10436                                   cls: 'fc-event-title',
10437                                   html : String.format('{0}', ev.title)
10438                                 }
10439                                 
10440                                 
10441                             ]
10442                         },
10443                         {
10444                             cls: 'ui-resizable-handle ui-resizable-e',
10445                             html : '&nbsp;&nbsp;&nbsp'
10446                         }
10447                         
10448                     ]
10449                 };
10450                 if (i == 0) {
10451                     cfg.cls += ' fc-event-start';
10452                 }
10453                 if ((i+1) == rows.length) {
10454                     cfg.cls += ' fc-event-end';
10455                 }
10456                 
10457                 var ctr = this.el.select('.fc-event-container',true).first();
10458                 var cg = ctr.createChild(cfg);
10459                 
10460                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10461                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10462                 cg.on('click', this.onEventClick, this, ev);
10463                 
10464                 ev.els.push(cg);
10465                 
10466                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10467                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10468                 //Roo.log(cg);
10469                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10470                 cg.setWidth(ebox.right - sbox.x -2);
10471             }
10472             
10473             
10474         }
10475         
10476     },
10477     
10478     onEventEnter: function (e, el,event,d) {
10479         this.fireEvent('evententer', this, el, event);
10480     },
10481     
10482     onEventLeave: function (e, el,event,d) {
10483         this.fireEvent('eventleave', this, el, event);
10484     },
10485     
10486     onEventClick: function (e, el,event,d) {
10487         this.fireEvent('eventclick', this, el, event);
10488     },
10489     
10490     onMonthChange: function () {
10491         this.store.load();
10492     },
10493     
10494     onLoad: function () 
10495     {   
10496         this.calevents = [];
10497         var cal = this;
10498         
10499         if(this.store.getCount() > 0){
10500             this.store.data.each(function(d){
10501                cal.addItem({
10502                     id : d.data.id,
10503                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10504                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10505                     time : d.data.start_time,
10506                     title : d.data.title,
10507                     description : d.data.description,
10508                     venue : d.data.venue
10509                 });
10510             });
10511         }
10512         
10513         this.renderEvents();
10514         
10515         if(this.loadMask){
10516             this.maskEl.hide();
10517         }
10518     },
10519     
10520     onBeforeLoad: function()
10521     {
10522         this.clearEvents();
10523         
10524         if(this.loadMask){
10525             this.maskEl.show();
10526         }
10527     }
10528 });
10529
10530  
10531  /*
10532  * - LGPL
10533  *
10534  * element
10535  * 
10536  */
10537
10538 /**
10539  * @class Roo.bootstrap.Popover
10540  * @extends Roo.bootstrap.Component
10541  * Bootstrap Popover class
10542  * @cfg {String} html contents of the popover   (or false to use children..)
10543  * @cfg {String} title of popover (or false to hide)
10544  * @cfg {String} placement how it is placed
10545  * @cfg {String} trigger click || hover (or false to trigger manually)
10546  * @cfg {String} over what (parent or false to trigger manually.)
10547  * 
10548  * @constructor
10549  * Create a new Popover
10550  * @param {Object} config The config object
10551  */
10552
10553 Roo.bootstrap.Popover = function(config){
10554     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10555 };
10556
10557 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10558     
10559     title: 'Fill in a title',
10560     html: false,
10561     
10562     placement : 'right',
10563     trigger : 'hover', // hover
10564     
10565     over: 'parent',
10566     
10567     can_build_overlaid : false,
10568     
10569     getChildContainer : function()
10570     {
10571         return this.el.select('.popover-content',true).first();
10572     },
10573     
10574     getAutoCreate : function(){
10575          Roo.log('make popover?');
10576         var cfg = {
10577            cls : 'popover roo-dynamic',
10578            style: 'display:block',
10579            cn : [
10580                 {
10581                     cls : 'arrow'
10582                 },
10583                 {
10584                     cls : 'popover-inner',
10585                     cn : [
10586                         {
10587                             tag: 'h3',
10588                             cls: 'popover-title',
10589                             html : this.title
10590                         },
10591                         {
10592                             cls : 'popover-content',
10593                             html : this.html
10594                         }
10595                     ]
10596                     
10597                 }
10598            ]
10599         };
10600         
10601         return cfg;
10602     },
10603     setTitle: function(str)
10604     {
10605         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10606     },
10607     setContent: function(str)
10608     {
10609         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10610     },
10611     // as it get's added to the bottom of the page.
10612     onRender : function(ct, position)
10613     {
10614         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10615         if(!this.el){
10616             var cfg = Roo.apply({},  this.getAutoCreate());
10617             cfg.id = Roo.id();
10618             
10619             if (this.cls) {
10620                 cfg.cls += ' ' + this.cls;
10621             }
10622             if (this.style) {
10623                 cfg.style = this.style;
10624             }
10625             Roo.log("adding to ")
10626             this.el = Roo.get(document.body).createChild(cfg, position);
10627             Roo.log(this.el);
10628         }
10629         this.initEvents();
10630     },
10631     
10632     initEvents : function()
10633     {
10634         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10635         this.el.enableDisplayMode('block');
10636         this.el.hide();
10637         if (this.over === false) {
10638             return; 
10639         }
10640         if (this.triggers === false) {
10641             return;
10642         }
10643         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10644         var triggers = this.trigger ? this.trigger.split(' ') : [];
10645         Roo.each(triggers, function(trigger) {
10646         
10647             if (trigger == 'click') {
10648                 on_el.on('click', this.toggle, this);
10649             } else if (trigger != 'manual') {
10650                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10651                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10652       
10653                 on_el.on(eventIn  ,this.enter, this);
10654                 on_el.on(eventOut, this.leave, this);
10655             }
10656         }, this);
10657         
10658     },
10659     
10660     
10661     // private
10662     timeout : null,
10663     hoverState : null,
10664     
10665     toggle : function () {
10666         this.hoverState == 'in' ? this.leave() : this.enter();
10667     },
10668     
10669     enter : function () {
10670        
10671     
10672         clearTimeout(this.timeout);
10673     
10674         this.hoverState = 'in'
10675     
10676         if (!this.delay || !this.delay.show) {
10677             this.show();
10678             return 
10679         }
10680         var _t = this;
10681         this.timeout = setTimeout(function () {
10682             if (_t.hoverState == 'in') {
10683                 _t.show();
10684             }
10685         }, this.delay.show)
10686     },
10687     leave : function() {
10688         clearTimeout(this.timeout);
10689     
10690         this.hoverState = 'out'
10691     
10692         if (!this.delay || !this.delay.hide) {
10693             this.hide();
10694             return 
10695         }
10696         var _t = this;
10697         this.timeout = setTimeout(function () {
10698             if (_t.hoverState == 'out') {
10699                 _t.hide();
10700             }
10701         }, this.delay.hide)
10702     },
10703     
10704     show : function (on_el)
10705     {
10706         if (!on_el) {
10707             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10708         }
10709         // set content.
10710         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10711         if (this.html !== false) {
10712             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10713         }
10714         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10715         if (!this.title.length) {
10716             this.el.select('.popover-title',true).hide();
10717         }
10718         
10719         var placement = typeof this.placement == 'function' ?
10720             this.placement.call(this, this.el, on_el) :
10721             this.placement;
10722             
10723         var autoToken = /\s?auto?\s?/i;
10724         var autoPlace = autoToken.test(placement);
10725         if (autoPlace) {
10726             placement = placement.replace(autoToken, '') || 'top';
10727         }
10728         
10729         //this.el.detach()
10730         //this.el.setXY([0,0]);
10731         this.el.show();
10732         this.el.dom.style.display='block';
10733         this.el.addClass(placement);
10734         
10735         //this.el.appendTo(on_el);
10736         
10737         var p = this.getPosition();
10738         var box = this.el.getBox();
10739         
10740         if (autoPlace) {
10741             // fixme..
10742         }
10743         var align = Roo.bootstrap.Popover.alignment[placement]
10744         this.el.alignTo(on_el, align[0],align[1]);
10745         //var arrow = this.el.select('.arrow',true).first();
10746         //arrow.set(align[2], 
10747         
10748         this.el.addClass('in');
10749         this.hoverState = null;
10750         
10751         if (this.el.hasClass('fade')) {
10752             // fade it?
10753         }
10754         
10755     },
10756     hide : function()
10757     {
10758         this.el.setXY([0,0]);
10759         this.el.removeClass('in');
10760         this.el.hide();
10761         
10762     }
10763     
10764 });
10765
10766 Roo.bootstrap.Popover.alignment = {
10767     'left' : ['r-l', [-10,0], 'right'],
10768     'right' : ['l-r', [10,0], 'left'],
10769     'bottom' : ['t-b', [0,10], 'top'],
10770     'top' : [ 'b-t', [0,-10], 'bottom']
10771 };
10772
10773  /*
10774  * - LGPL
10775  *
10776  * Progress
10777  * 
10778  */
10779
10780 /**
10781  * @class Roo.bootstrap.Progress
10782  * @extends Roo.bootstrap.Component
10783  * Bootstrap Progress class
10784  * @cfg {Boolean} striped striped of the progress bar
10785  * @cfg {Boolean} active animated of the progress bar
10786  * 
10787  * 
10788  * @constructor
10789  * Create a new Progress
10790  * @param {Object} config The config object
10791  */
10792
10793 Roo.bootstrap.Progress = function(config){
10794     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10795 };
10796
10797 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10798     
10799     striped : false,
10800     active: false,
10801     
10802     getAutoCreate : function(){
10803         var cfg = {
10804             tag: 'div',
10805             cls: 'progress'
10806         };
10807         
10808         
10809         if(this.striped){
10810             cfg.cls += ' progress-striped';
10811         }
10812       
10813         if(this.active){
10814             cfg.cls += ' active';
10815         }
10816         
10817         
10818         return cfg;
10819     }
10820    
10821 });
10822
10823  
10824
10825  /*
10826  * - LGPL
10827  *
10828  * ProgressBar
10829  * 
10830  */
10831
10832 /**
10833  * @class Roo.bootstrap.ProgressBar
10834  * @extends Roo.bootstrap.Component
10835  * Bootstrap ProgressBar class
10836  * @cfg {Number} aria_valuenow aria-value now
10837  * @cfg {Number} aria_valuemin aria-value min
10838  * @cfg {Number} aria_valuemax aria-value max
10839  * @cfg {String} label label for the progress bar
10840  * @cfg {String} panel (success | info | warning | danger )
10841  * @cfg {String} role role of the progress bar
10842  * @cfg {String} sr_only text
10843  * 
10844  * 
10845  * @constructor
10846  * Create a new ProgressBar
10847  * @param {Object} config The config object
10848  */
10849
10850 Roo.bootstrap.ProgressBar = function(config){
10851     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10852 };
10853
10854 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10855     
10856     aria_valuenow : 0,
10857     aria_valuemin : 0,
10858     aria_valuemax : 100,
10859     label : false,
10860     panel : false,
10861     role : false,
10862     sr_only: false,
10863     
10864     getAutoCreate : function()
10865     {
10866         
10867         var cfg = {
10868             tag: 'div',
10869             cls: 'progress-bar',
10870             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10871         };
10872         
10873         if(this.sr_only){
10874             cfg.cn = {
10875                 tag: 'span',
10876                 cls: 'sr-only',
10877                 html: this.sr_only
10878             }
10879         }
10880         
10881         if(this.role){
10882             cfg.role = this.role;
10883         }
10884         
10885         if(this.aria_valuenow){
10886             cfg['aria-valuenow'] = this.aria_valuenow;
10887         }
10888         
10889         if(this.aria_valuemin){
10890             cfg['aria-valuemin'] = this.aria_valuemin;
10891         }
10892         
10893         if(this.aria_valuemax){
10894             cfg['aria-valuemax'] = this.aria_valuemax;
10895         }
10896         
10897         if(this.label && !this.sr_only){
10898             cfg.html = this.label;
10899         }
10900         
10901         if(this.panel){
10902             cfg.cls += ' progress-bar-' + this.panel;
10903         }
10904         
10905         return cfg;
10906     },
10907     
10908     update : function(aria_valuenow)
10909     {
10910         this.aria_valuenow = aria_valuenow;
10911         
10912         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10913     }
10914    
10915 });
10916
10917  
10918
10919  /*
10920  * - LGPL
10921  *
10922  * TabPanel
10923  * 
10924  */
10925
10926 /**
10927  * @class Roo.bootstrap.TabPanel
10928  * @extends Roo.bootstrap.Component
10929  * Bootstrap TabPanel class
10930  * @cfg {Boolean} active panel active
10931  * @cfg {String} html panel content
10932  * @cfg {String} tabId tab relate id
10933  * 
10934  * 
10935  * @constructor
10936  * Create a new TabPanel
10937  * @param {Object} config The config object
10938  */
10939
10940 Roo.bootstrap.TabPanel = function(config){
10941     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10942 };
10943
10944 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10945     
10946     active: false,
10947     html: false,
10948     tabId: false,
10949     
10950     getAutoCreate : function(){
10951         var cfg = {
10952             tag: 'div',
10953             cls: 'tab-pane',
10954             html: this.html || ''
10955         };
10956         
10957         if(this.active){
10958             cfg.cls += ' active';
10959         }
10960         
10961         if(this.tabId){
10962             cfg.tabId = this.tabId;
10963         }
10964         
10965         return cfg;
10966     }
10967    
10968 });
10969
10970  
10971
10972  /*
10973  * - LGPL
10974  *
10975  * DateField
10976  * 
10977  */
10978
10979 /**
10980  * @class Roo.bootstrap.DateField
10981  * @extends Roo.bootstrap.Input
10982  * Bootstrap DateField class
10983  * @cfg {Number} weekStart default 0
10984  * @cfg {Number} weekStart default 0
10985  * @cfg {Number} viewMode default empty, (months|years)
10986  * @cfg {Number} minViewMode default empty, (months|years)
10987  * @cfg {Number} startDate default -Infinity
10988  * @cfg {Number} endDate default Infinity
10989  * @cfg {Boolean} todayHighlight default false
10990  * @cfg {Boolean} todayBtn default false
10991  * @cfg {Boolean} calendarWeeks default false
10992  * @cfg {Object} daysOfWeekDisabled default empty
10993  * 
10994  * @cfg {Boolean} keyboardNavigation default true
10995  * @cfg {String} language default en
10996  * 
10997  * @constructor
10998  * Create a new DateField
10999  * @param {Object} config The config object
11000  */
11001
11002 Roo.bootstrap.DateField = function(config){
11003     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11004      this.addEvents({
11005             /**
11006              * @event show
11007              * Fires when this field show.
11008              * @param {Roo.bootstrap.DateField} this
11009              * @param {Mixed} date The date value
11010              */
11011             show : true,
11012             /**
11013              * @event show
11014              * Fires when this field hide.
11015              * @param {Roo.bootstrap.DateField} this
11016              * @param {Mixed} date The date value
11017              */
11018             hide : true,
11019             /**
11020              * @event select
11021              * Fires when select a date.
11022              * @param {Roo.bootstrap.DateField} this
11023              * @param {Mixed} date The date value
11024              */
11025             select : true
11026         });
11027 };
11028
11029 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11030     
11031     /**
11032      * @cfg {String} format
11033      * The default date format string which can be overriden for localization support.  The format must be
11034      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11035      */
11036     format : "m/d/y",
11037     /**
11038      * @cfg {String} altFormats
11039      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11040      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11041      */
11042     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11043     
11044     weekStart : 0,
11045     
11046     viewMode : '',
11047     
11048     minViewMode : '',
11049     
11050     todayHighlight : false,
11051     
11052     todayBtn: false,
11053     
11054     language: 'en',
11055     
11056     keyboardNavigation: true,
11057     
11058     calendarWeeks: false,
11059     
11060     startDate: -Infinity,
11061     
11062     endDate: Infinity,
11063     
11064     daysOfWeekDisabled: [],
11065     
11066     _events: [],
11067     
11068     UTCDate: function()
11069     {
11070         return new Date(Date.UTC.apply(Date, arguments));
11071     },
11072     
11073     UTCToday: function()
11074     {
11075         var today = new Date();
11076         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11077     },
11078     
11079     getDate: function() {
11080             var d = this.getUTCDate();
11081             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11082     },
11083     
11084     getUTCDate: function() {
11085             return this.date;
11086     },
11087     
11088     setDate: function(d) {
11089             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11090     },
11091     
11092     setUTCDate: function(d) {
11093             this.date = d;
11094             this.setValue(this.formatDate(this.date));
11095     },
11096         
11097     onRender: function(ct, position)
11098     {
11099         
11100         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11101         
11102         this.language = this.language || 'en';
11103         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11104         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11105         
11106         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11107         this.format = this.format || 'm/d/y';
11108         this.isInline = false;
11109         this.isInput = true;
11110         this.component = this.el.select('.add-on', true).first() || false;
11111         this.component = (this.component && this.component.length === 0) ? false : this.component;
11112         this.hasInput = this.component && this.inputEL().length;
11113         
11114         if (typeof(this.minViewMode === 'string')) {
11115             switch (this.minViewMode) {
11116                 case 'months':
11117                     this.minViewMode = 1;
11118                     break;
11119                 case 'years':
11120                     this.minViewMode = 2;
11121                     break;
11122                 default:
11123                     this.minViewMode = 0;
11124                     break;
11125             }
11126         }
11127         
11128         if (typeof(this.viewMode === 'string')) {
11129             switch (this.viewMode) {
11130                 case 'months':
11131                     this.viewMode = 1;
11132                     break;
11133                 case 'years':
11134                     this.viewMode = 2;
11135                     break;
11136                 default:
11137                     this.viewMode = 0;
11138                     break;
11139             }
11140         }
11141                 
11142         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11143         
11144         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11145         
11146         this.picker().on('mousedown', this.onMousedown, this);
11147         this.picker().on('click', this.onClick, this);
11148         
11149         this.picker().addClass('datepicker-dropdown');
11150         
11151         this.startViewMode = this.viewMode;
11152         
11153         
11154         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11155             if(!this.calendarWeeks){
11156                 v.remove();
11157                 return;
11158             };
11159             
11160             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11161             v.attr('colspan', function(i, val){
11162                 return parseInt(val) + 1;
11163             });
11164         })
11165                         
11166         
11167         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11168         
11169         this.setStartDate(this.startDate);
11170         this.setEndDate(this.endDate);
11171         
11172         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11173         
11174         this.fillDow();
11175         this.fillMonths();
11176         this.update();
11177         this.showMode();
11178         
11179         if(this.isInline) {
11180             this.show();
11181         }
11182     },
11183     
11184     picker : function()
11185     {
11186         return this.el.select('.datepicker', true).first();
11187     },
11188     
11189     fillDow: function()
11190     {
11191         var dowCnt = this.weekStart;
11192         
11193         var dow = {
11194             tag: 'tr',
11195             cn: [
11196                 
11197             ]
11198         };
11199         
11200         if(this.calendarWeeks){
11201             dow.cn.push({
11202                 tag: 'th',
11203                 cls: 'cw',
11204                 html: '&nbsp;'
11205             })
11206         }
11207         
11208         while (dowCnt < this.weekStart + 7) {
11209             dow.cn.push({
11210                 tag: 'th',
11211                 cls: 'dow',
11212                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11213             });
11214         }
11215         
11216         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11217     },
11218     
11219     fillMonths: function()
11220     {    
11221         var i = 0
11222         var months = this.picker().select('>.datepicker-months td', true).first();
11223         
11224         months.dom.innerHTML = '';
11225         
11226         while (i < 12) {
11227             var month = {
11228                 tag: 'span',
11229                 cls: 'month',
11230                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11231             }
11232             
11233             months.createChild(month);
11234         }
11235         
11236     },
11237     
11238     update: function(){
11239         
11240         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11241         
11242         if (this.date < this.startDate) {
11243             this.viewDate = new Date(this.startDate);
11244         } else if (this.date > this.endDate) {
11245             this.viewDate = new Date(this.endDate);
11246         } else {
11247             this.viewDate = new Date(this.date);
11248         }
11249         
11250         this.fill();
11251     },
11252     
11253     fill: function() {
11254         var d = new Date(this.viewDate),
11255                 year = d.getUTCFullYear(),
11256                 month = d.getUTCMonth(),
11257                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11258                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11259                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11260                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11261                 currentDate = this.date && this.date.valueOf(),
11262                 today = this.UTCToday();
11263         
11264         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11265         
11266 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11267         
11268 //        this.picker.select('>tfoot th.today').
11269 //                                              .text(dates[this.language].today)
11270 //                                              .toggle(this.todayBtn !== false);
11271     
11272         this.updateNavArrows();
11273         this.fillMonths();
11274                                                 
11275         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11276         
11277         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11278          
11279         prevMonth.setUTCDate(day);
11280         
11281         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11282         
11283         var nextMonth = new Date(prevMonth);
11284         
11285         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11286         
11287         nextMonth = nextMonth.valueOf();
11288         
11289         var fillMonths = false;
11290         
11291         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11292         
11293         while(prevMonth.valueOf() < nextMonth) {
11294             var clsName = '';
11295             
11296             if (prevMonth.getUTCDay() === this.weekStart) {
11297                 if(fillMonths){
11298                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11299                 }
11300                     
11301                 fillMonths = {
11302                     tag: 'tr',
11303                     cn: []
11304                 };
11305                 
11306                 if(this.calendarWeeks){
11307                     // ISO 8601: First week contains first thursday.
11308                     // ISO also states week starts on Monday, but we can be more abstract here.
11309                     var
11310                     // Start of current week: based on weekstart/current date
11311                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11312                     // Thursday of this week
11313                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11314                     // First Thursday of year, year from thursday
11315                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11316                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11317                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11318                     
11319                     fillMonths.cn.push({
11320                         tag: 'td',
11321                         cls: 'cw',
11322                         html: calWeek
11323                     });
11324                 }
11325             }
11326             
11327             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11328                 clsName += ' old';
11329             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11330                 clsName += ' new';
11331             }
11332             if (this.todayHighlight &&
11333                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11334                 prevMonth.getUTCMonth() == today.getMonth() &&
11335                 prevMonth.getUTCDate() == today.getDate()) {
11336                 clsName += ' today';
11337             }
11338             
11339             if (currentDate && prevMonth.valueOf() === currentDate) {
11340                 clsName += ' active';
11341             }
11342             
11343             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11344                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11345                     clsName += ' disabled';
11346             }
11347             
11348             fillMonths.cn.push({
11349                 tag: 'td',
11350                 cls: 'day ' + clsName,
11351                 html: prevMonth.getDate()
11352             })
11353             
11354             prevMonth.setDate(prevMonth.getDate()+1);
11355         }
11356           
11357         var currentYear = this.date && this.date.getUTCFullYear();
11358         var currentMonth = this.date && this.date.getUTCMonth();
11359         
11360         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11361         
11362         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11363             v.removeClass('active');
11364             
11365             if(currentYear === year && k === currentMonth){
11366                 v.addClass('active');
11367             }
11368             
11369             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11370                 v.addClass('disabled');
11371             }
11372             
11373         });
11374         
11375         
11376         year = parseInt(year/10, 10) * 10;
11377         
11378         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11379         
11380         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11381         
11382         year -= 1;
11383         for (var i = -1; i < 11; i++) {
11384             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11385                 tag: 'span',
11386                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11387                 html: year
11388             })
11389             
11390             year += 1;
11391         }
11392     },
11393     
11394     showMode: function(dir) {
11395         if (dir) {
11396             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11397         }
11398         Roo.each(this.picker().select('>div',true).elements, function(v){
11399             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11400             v.hide();
11401         });
11402         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11403     },
11404     
11405     place: function()
11406     {
11407         if(this.isInline) return;
11408         
11409         this.picker().removeClass(['bottom', 'top']);
11410         
11411         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11412             /*
11413              * place to the top of element!
11414              *
11415              */
11416             
11417             this.picker().addClass('top');
11418             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11419             
11420             return;
11421         }
11422         
11423         this.picker().addClass('bottom');
11424         
11425         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11426     },
11427     
11428     parseDate : function(value){
11429         if(!value || value instanceof Date){
11430             return value;
11431         }
11432         var v = Date.parseDate(value, this.format);
11433         if (!v && this.useIso) {
11434             v = Date.parseDate(value, 'Y-m-d');
11435         }
11436         if(!v && this.altFormats){
11437             if(!this.altFormatsArray){
11438                 this.altFormatsArray = this.altFormats.split("|");
11439             }
11440             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11441                 v = Date.parseDate(value, this.altFormatsArray[i]);
11442             }
11443         }
11444         return v;
11445     },
11446     
11447     formatDate : function(date, fmt){
11448         return (!date || !(date instanceof Date)) ?
11449         date : date.dateFormat(fmt || this.format);
11450     },
11451     
11452     onFocus : function()
11453     {
11454         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11455         this.show();
11456     },
11457     
11458     onBlur : function()
11459     {
11460         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11461         this.hide();
11462     },
11463     
11464     show : function()
11465     {
11466         this.picker().show();
11467         this.update();
11468         this.place();
11469         
11470         this.fireEvent('show', this, this.date);
11471     },
11472     
11473     hide : function()
11474     {
11475         if(this.isInline) return;
11476         this.picker().hide();
11477         this.viewMode = this.startViewMode;
11478         this.showMode();
11479         
11480         this.fireEvent('hide', this, this.date);
11481         
11482     },
11483     
11484     onMousedown: function(e){
11485         e.stopPropagation();
11486         e.preventDefault();
11487     },
11488     
11489     keyup: function(e){
11490         Roo.bootstrap.DateField.superclass.keyup.call(this);
11491         this.update();
11492         
11493     },
11494
11495     setValue: function(v){
11496         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11497         
11498         this.fireEvent('select', this, this.date);
11499         
11500     },
11501     
11502     fireKey: function(e){
11503         if (!this.picker().isVisible()){
11504             if (e.keyCode == 27) // allow escape to hide and re-show picker
11505                 this.show();
11506             return;
11507         }
11508         var dateChanged = false,
11509         dir, day, month,
11510         newDate, newViewDate;
11511         switch(e.keyCode){
11512             case 27: // escape
11513                 this.hide();
11514                 e.preventDefault();
11515                 break;
11516             case 37: // left
11517             case 39: // right
11518                 if (!this.keyboardNavigation) break;
11519                 dir = e.keyCode == 37 ? -1 : 1;
11520                 
11521                 if (e.ctrlKey){
11522                     newDate = this.moveYear(this.date, dir);
11523                     newViewDate = this.moveYear(this.viewDate, dir);
11524                 } else if (e.shiftKey){
11525                     newDate = this.moveMonth(this.date, dir);
11526                     newViewDate = this.moveMonth(this.viewDate, dir);
11527                 } else {
11528                     newDate = new Date(this.date);
11529                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11530                     newViewDate = new Date(this.viewDate);
11531                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11532                 }
11533                 if (this.dateWithinRange(newDate)){
11534                     this.date = newDate;
11535                     this.viewDate = newViewDate;
11536                     this.setValue(this.formatDate(this.date));
11537                     this.update();
11538                     e.preventDefault();
11539                     dateChanged = true;
11540                 }
11541                 break;
11542             case 38: // up
11543             case 40: // down
11544                 if (!this.keyboardNavigation) break;
11545                 dir = e.keyCode == 38 ? -1 : 1;
11546                 if (e.ctrlKey){
11547                     newDate = this.moveYear(this.date, dir);
11548                     newViewDate = this.moveYear(this.viewDate, dir);
11549                 } else if (e.shiftKey){
11550                     newDate = this.moveMonth(this.date, dir);
11551                     newViewDate = this.moveMonth(this.viewDate, dir);
11552                 } else {
11553                     newDate = new Date(this.date);
11554                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11555                     newViewDate = new Date(this.viewDate);
11556                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11557                 }
11558                 if (this.dateWithinRange(newDate)){
11559                     this.date = newDate;
11560                     this.viewDate = newViewDate;
11561                     this.setValue(this.formatDate(this.date));
11562                     this.update();
11563                     e.preventDefault();
11564                     dateChanged = true;
11565                 }
11566                 break;
11567             case 13: // enter
11568                 this.setValue(this.formatDate(this.date));
11569                 this.hide();
11570                 e.preventDefault();
11571                 break;
11572             case 9: // tab
11573                 this.setValue(this.formatDate(this.date));
11574                 this.hide();
11575                 break;
11576         }
11577     },
11578     
11579     
11580     onClick: function(e) {
11581         e.stopPropagation();
11582         e.preventDefault();
11583         
11584         var target = e.getTarget();
11585         
11586         if(target.nodeName.toLowerCase() === 'i'){
11587             target = Roo.get(target).dom.parentNode;
11588         }
11589         
11590         var nodeName = target.nodeName;
11591         var className = target.className;
11592         var html = target.innerHTML;
11593         
11594         switch(nodeName.toLowerCase()) {
11595             case 'th':
11596                 switch(className) {
11597                     case 'switch':
11598                         this.showMode(1);
11599                         break;
11600                     case 'prev':
11601                     case 'next':
11602                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11603                         switch(this.viewMode){
11604                                 case 0:
11605                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11606                                         break;
11607                                 case 1:
11608                                 case 2:
11609                                         this.viewDate = this.moveYear(this.viewDate, dir);
11610                                         break;
11611                         }
11612                         this.fill();
11613                         break;
11614                     case 'today':
11615                         var date = new Date();
11616                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11617                         this.fill()
11618                         this.setValue(this.formatDate(this.date));
11619                         this.hide();
11620                         break;
11621                 }
11622                 break;
11623             case 'span':
11624                 if (className.indexOf('disabled') === -1) {
11625                     this.viewDate.setUTCDate(1);
11626                     if (className.indexOf('month') !== -1) {
11627                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11628                     } else {
11629                         var year = parseInt(html, 10) || 0;
11630                         this.viewDate.setUTCFullYear(year);
11631                         
11632                     }
11633                     this.showMode(-1);
11634                     this.fill();
11635                 }
11636                 break;
11637                 
11638             case 'td':
11639                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11640                     var day = parseInt(html, 10) || 1;
11641                     var year = this.viewDate.getUTCFullYear(),
11642                         month = this.viewDate.getUTCMonth();
11643
11644                     if (className.indexOf('old') !== -1) {
11645                         if(month === 0 ){
11646                             month = 11;
11647                             year -= 1;
11648                         }else{
11649                             month -= 1;
11650                         }
11651                     } else if (className.indexOf('new') !== -1) {
11652                         if (month == 11) {
11653                             month = 0;
11654                             year += 1;
11655                         } else {
11656                             month += 1;
11657                         }
11658                     }
11659                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11660                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11661                     this.fill();
11662                     this.setValue(this.formatDate(this.date));
11663                     this.hide();
11664                 }
11665                 break;
11666         }
11667     },
11668     
11669     setStartDate: function(startDate){
11670         this.startDate = startDate || -Infinity;
11671         if (this.startDate !== -Infinity) {
11672             this.startDate = this.parseDate(this.startDate);
11673         }
11674         this.update();
11675         this.updateNavArrows();
11676     },
11677
11678     setEndDate: function(endDate){
11679         this.endDate = endDate || Infinity;
11680         if (this.endDate !== Infinity) {
11681             this.endDate = this.parseDate(this.endDate);
11682         }
11683         this.update();
11684         this.updateNavArrows();
11685     },
11686     
11687     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11688         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11689         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11690             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11691         }
11692         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11693             return parseInt(d, 10);
11694         });
11695         this.update();
11696         this.updateNavArrows();
11697     },
11698     
11699     updateNavArrows: function() {
11700         var d = new Date(this.viewDate),
11701         year = d.getUTCFullYear(),
11702         month = d.getUTCMonth();
11703         
11704         Roo.each(this.picker().select('.prev', true).elements, function(v){
11705             v.show();
11706             switch (this.viewMode) {
11707                 case 0:
11708
11709                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11710                         v.hide();
11711                     }
11712                     break;
11713                 case 1:
11714                 case 2:
11715                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11716                         v.hide();
11717                     }
11718                     break;
11719             }
11720         });
11721         
11722         Roo.each(this.picker().select('.next', true).elements, function(v){
11723             v.show();
11724             switch (this.viewMode) {
11725                 case 0:
11726
11727                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11728                         v.hide();
11729                     }
11730                     break;
11731                 case 1:
11732                 case 2:
11733                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11734                         v.hide();
11735                     }
11736                     break;
11737             }
11738         })
11739     },
11740     
11741     moveMonth: function(date, dir){
11742         if (!dir) return date;
11743         var new_date = new Date(date.valueOf()),
11744         day = new_date.getUTCDate(),
11745         month = new_date.getUTCMonth(),
11746         mag = Math.abs(dir),
11747         new_month, test;
11748         dir = dir > 0 ? 1 : -1;
11749         if (mag == 1){
11750             test = dir == -1
11751             // If going back one month, make sure month is not current month
11752             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11753             ? function(){
11754                 return new_date.getUTCMonth() == month;
11755             }
11756             // If going forward one month, make sure month is as expected
11757             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11758             : function(){
11759                 return new_date.getUTCMonth() != new_month;
11760             };
11761             new_month = month + dir;
11762             new_date.setUTCMonth(new_month);
11763             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11764             if (new_month < 0 || new_month > 11)
11765                 new_month = (new_month + 12) % 12;
11766         } else {
11767             // For magnitudes >1, move one month at a time...
11768             for (var i=0; i<mag; i++)
11769                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11770                 new_date = this.moveMonth(new_date, dir);
11771             // ...then reset the day, keeping it in the new month
11772             new_month = new_date.getUTCMonth();
11773             new_date.setUTCDate(day);
11774             test = function(){
11775                 return new_month != new_date.getUTCMonth();
11776             };
11777         }
11778         // Common date-resetting loop -- if date is beyond end of month, make it
11779         // end of month
11780         while (test()){
11781             new_date.setUTCDate(--day);
11782             new_date.setUTCMonth(new_month);
11783         }
11784         return new_date;
11785     },
11786
11787     moveYear: function(date, dir){
11788         return this.moveMonth(date, dir*12);
11789     },
11790
11791     dateWithinRange: function(date){
11792         return date >= this.startDate && date <= this.endDate;
11793     },
11794
11795     
11796     remove: function() {
11797         this.picker().remove();
11798     }
11799    
11800 });
11801
11802 Roo.apply(Roo.bootstrap.DateField,  {
11803     
11804     head : {
11805         tag: 'thead',
11806         cn: [
11807         {
11808             tag: 'tr',
11809             cn: [
11810             {
11811                 tag: 'th',
11812                 cls: 'prev',
11813                 html: '<i class="icon-arrow-left"/>'
11814             },
11815             {
11816                 tag: 'th',
11817                 cls: 'switch',
11818                 colspan: '5'
11819             },
11820             {
11821                 tag: 'th',
11822                 cls: 'next',
11823                 html: '<i class="icon-arrow-right"/>'
11824             }
11825
11826             ]
11827         }
11828         ]
11829     },
11830     
11831     content : {
11832         tag: 'tbody',
11833         cn: [
11834         {
11835             tag: 'tr',
11836             cn: [
11837             {
11838                 tag: 'td',
11839                 colspan: '7'
11840             }
11841             ]
11842         }
11843         ]
11844     },
11845     
11846     footer : {
11847         tag: 'tfoot',
11848         cn: [
11849         {
11850             tag: 'tr',
11851             cn: [
11852             {
11853                 tag: 'th',
11854                 colspan: '7',
11855                 cls: 'today'
11856             }
11857                     
11858             ]
11859         }
11860         ]
11861     },
11862     
11863     dates:{
11864         en: {
11865             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11866             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11867             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11868             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11869             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11870             today: "Today"
11871         }
11872     },
11873     
11874     modes: [
11875     {
11876         clsName: 'days',
11877         navFnc: 'Month',
11878         navStep: 1
11879     },
11880     {
11881         clsName: 'months',
11882         navFnc: 'FullYear',
11883         navStep: 1
11884     },
11885     {
11886         clsName: 'years',
11887         navFnc: 'FullYear',
11888         navStep: 10
11889     }]
11890 });
11891
11892 Roo.apply(Roo.bootstrap.DateField,  {
11893   
11894     template : {
11895         tag: 'div',
11896         cls: 'datepicker dropdown-menu',
11897         cn: [
11898         {
11899             tag: 'div',
11900             cls: 'datepicker-days',
11901             cn: [
11902             {
11903                 tag: 'table',
11904                 cls: 'table-condensed',
11905                 cn:[
11906                 Roo.bootstrap.DateField.head,
11907                 {
11908                     tag: 'tbody'
11909                 },
11910                 Roo.bootstrap.DateField.footer
11911                 ]
11912             }
11913             ]
11914         },
11915         {
11916             tag: 'div',
11917             cls: 'datepicker-months',
11918             cn: [
11919             {
11920                 tag: 'table',
11921                 cls: 'table-condensed',
11922                 cn:[
11923                 Roo.bootstrap.DateField.head,
11924                 Roo.bootstrap.DateField.content,
11925                 Roo.bootstrap.DateField.footer
11926                 ]
11927             }
11928             ]
11929         },
11930         {
11931             tag: 'div',
11932             cls: 'datepicker-years',
11933             cn: [
11934             {
11935                 tag: 'table',
11936                 cls: 'table-condensed',
11937                 cn:[
11938                 Roo.bootstrap.DateField.head,
11939                 Roo.bootstrap.DateField.content,
11940                 Roo.bootstrap.DateField.footer
11941                 ]
11942             }
11943             ]
11944         }
11945         ]
11946     }
11947 });
11948
11949  
11950
11951  /*
11952  * - LGPL
11953  *
11954  * TimeField
11955  * 
11956  */
11957
11958 /**
11959  * @class Roo.bootstrap.TimeField
11960  * @extends Roo.bootstrap.Input
11961  * Bootstrap DateField class
11962  * 
11963  * 
11964  * @constructor
11965  * Create a new TimeField
11966  * @param {Object} config The config object
11967  */
11968
11969 Roo.bootstrap.TimeField = function(config){
11970     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11971     this.addEvents({
11972             /**
11973              * @event show
11974              * Fires when this field show.
11975              * @param {Roo.bootstrap.DateField} this
11976              * @param {Mixed} date The date value
11977              */
11978             show : true,
11979             /**
11980              * @event show
11981              * Fires when this field hide.
11982              * @param {Roo.bootstrap.DateField} this
11983              * @param {Mixed} date The date value
11984              */
11985             hide : true,
11986             /**
11987              * @event select
11988              * Fires when select a date.
11989              * @param {Roo.bootstrap.DateField} this
11990              * @param {Mixed} date The date value
11991              */
11992             select : true
11993         });
11994 };
11995
11996 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11997     
11998     /**
11999      * @cfg {String} format
12000      * The default time format string which can be overriden for localization support.  The format must be
12001      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12002      */
12003     format : "H:i",
12004        
12005     onRender: function(ct, position)
12006     {
12007         
12008         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12009                 
12010         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12011         
12012         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12013         
12014         this.pop = this.picker().select('>.datepicker-time',true).first();
12015         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12016         
12017         this.picker().on('mousedown', this.onMousedown, this);
12018         this.picker().on('click', this.onClick, this);
12019         
12020         this.picker().addClass('datepicker-dropdown');
12021     
12022         this.fillTime();
12023         this.update();
12024             
12025         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12026         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12027         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12028         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12029         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12030         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12031
12032     },
12033     
12034     fireKey: function(e){
12035         if (!this.picker().isVisible()){
12036             if (e.keyCode == 27) // allow escape to hide and re-show picker
12037                 this.show();
12038             return;
12039         }
12040
12041         e.preventDefault();
12042         
12043         switch(e.keyCode){
12044             case 27: // escape
12045                 this.hide();
12046                 break;
12047             case 37: // left
12048             case 39: // right
12049                 this.onTogglePeriod();
12050                 break;
12051             case 38: // up
12052                 this.onIncrementMinutes();
12053                 break;
12054             case 40: // down
12055                 this.onDecrementMinutes();
12056                 break;
12057             case 13: // enter
12058             case 9: // tab
12059                 this.setTime();
12060                 break;
12061         }
12062     },
12063     
12064     onClick: function(e) {
12065         e.stopPropagation();
12066         e.preventDefault();
12067     },
12068     
12069     picker : function()
12070     {
12071         return this.el.select('.datepicker', true).first();
12072     },
12073     
12074     fillTime: function()
12075     {    
12076         var time = this.pop.select('tbody', true).first();
12077         
12078         time.dom.innerHTML = '';
12079         
12080         time.createChild({
12081             tag: 'tr',
12082             cn: [
12083                 {
12084                     tag: 'td',
12085                     cn: [
12086                         {
12087                             tag: 'a',
12088                             href: '#',
12089                             cls: 'btn',
12090                             cn: [
12091                                 {
12092                                     tag: 'span',
12093                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12094                                 }
12095                             ]
12096                         } 
12097                     ]
12098                 },
12099                 {
12100                     tag: 'td',
12101                     cls: 'separator'
12102                 },
12103                 {
12104                     tag: 'td',
12105                     cn: [
12106                         {
12107                             tag: 'a',
12108                             href: '#',
12109                             cls: 'btn',
12110                             cn: [
12111                                 {
12112                                     tag: 'span',
12113                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12114                                 }
12115                             ]
12116                         }
12117                     ]
12118                 },
12119                 {
12120                     tag: 'td',
12121                     cls: 'separator'
12122                 }
12123             ]
12124         });
12125         
12126         time.createChild({
12127             tag: 'tr',
12128             cn: [
12129                 {
12130                     tag: 'td',
12131                     cn: [
12132                         {
12133                             tag: 'span',
12134                             cls: 'timepicker-hour',
12135                             html: '00'
12136                         }  
12137                     ]
12138                 },
12139                 {
12140                     tag: 'td',
12141                     cls: 'separator',
12142                     html: ':'
12143                 },
12144                 {
12145                     tag: 'td',
12146                     cn: [
12147                         {
12148                             tag: 'span',
12149                             cls: 'timepicker-minute',
12150                             html: '00'
12151                         }  
12152                     ]
12153                 },
12154                 {
12155                     tag: 'td',
12156                     cls: 'separator'
12157                 },
12158                 {
12159                     tag: 'td',
12160                     cn: [
12161                         {
12162                             tag: 'button',
12163                             type: 'button',
12164                             cls: 'btn btn-primary period',
12165                             html: 'AM'
12166                             
12167                         }
12168                     ]
12169                 }
12170             ]
12171         });
12172         
12173         time.createChild({
12174             tag: 'tr',
12175             cn: [
12176                 {
12177                     tag: 'td',
12178                     cn: [
12179                         {
12180                             tag: 'a',
12181                             href: '#',
12182                             cls: 'btn',
12183                             cn: [
12184                                 {
12185                                     tag: 'span',
12186                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12187                                 }
12188                             ]
12189                         }
12190                     ]
12191                 },
12192                 {
12193                     tag: 'td',
12194                     cls: 'separator'
12195                 },
12196                 {
12197                     tag: 'td',
12198                     cn: [
12199                         {
12200                             tag: 'a',
12201                             href: '#',
12202                             cls: 'btn',
12203                             cn: [
12204                                 {
12205                                     tag: 'span',
12206                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12207                                 }
12208                             ]
12209                         }
12210                     ]
12211                 },
12212                 {
12213                     tag: 'td',
12214                     cls: 'separator'
12215                 }
12216             ]
12217         });
12218         
12219     },
12220     
12221     update: function()
12222     {
12223         
12224         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12225         
12226         this.fill();
12227     },
12228     
12229     fill: function() 
12230     {
12231         var hours = this.time.getHours();
12232         var minutes = this.time.getMinutes();
12233         var period = 'AM';
12234         
12235         if(hours > 11){
12236             period = 'PM';
12237         }
12238         
12239         if(hours == 0){
12240             hours = 12;
12241         }
12242         
12243         
12244         if(hours > 12){
12245             hours = hours - 12;
12246         }
12247         
12248         if(hours < 10){
12249             hours = '0' + hours;
12250         }
12251         
12252         if(minutes < 10){
12253             minutes = '0' + minutes;
12254         }
12255         
12256         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12257         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12258         this.pop.select('button', true).first().dom.innerHTML = period;
12259         
12260     },
12261     
12262     place: function()
12263     {   
12264         this.picker().removeClass(['bottom', 'top']);
12265         
12266         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12267             /*
12268              * place to the top of element!
12269              *
12270              */
12271             
12272             this.picker().addClass('top');
12273             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12274             
12275             return;
12276         }
12277         
12278         this.picker().addClass('bottom');
12279         
12280         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12281     },
12282   
12283     onFocus : function()
12284     {
12285         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12286         this.show();
12287     },
12288     
12289     onBlur : function()
12290     {
12291         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12292         this.hide();
12293     },
12294     
12295     show : function()
12296     {
12297         this.picker().show();
12298         this.pop.show();
12299         this.update();
12300         this.place();
12301         
12302         this.fireEvent('show', this, this.date);
12303     },
12304     
12305     hide : function()
12306     {
12307         this.picker().hide();
12308         this.pop.hide();
12309         
12310         this.fireEvent('hide', this, this.date);
12311     },
12312     
12313     setTime : function()
12314     {
12315         this.hide();
12316         this.setValue(this.time.format(this.format));
12317         
12318         this.fireEvent('select', this, this.date);
12319         
12320         
12321     },
12322     
12323     onMousedown: function(e){
12324         e.stopPropagation();
12325         e.preventDefault();
12326     },
12327     
12328     onIncrementHours: function()
12329     {
12330         Roo.log('onIncrementHours');
12331         this.time = this.time.add(Date.HOUR, 1);
12332         this.update();
12333         
12334     },
12335     
12336     onDecrementHours: function()
12337     {
12338         Roo.log('onDecrementHours');
12339         this.time = this.time.add(Date.HOUR, -1);
12340         this.update();
12341     },
12342     
12343     onIncrementMinutes: function()
12344     {
12345         Roo.log('onIncrementMinutes');
12346         this.time = this.time.add(Date.MINUTE, 1);
12347         this.update();
12348     },
12349     
12350     onDecrementMinutes: function()
12351     {
12352         Roo.log('onDecrementMinutes');
12353         this.time = this.time.add(Date.MINUTE, -1);
12354         this.update();
12355     },
12356     
12357     onTogglePeriod: function()
12358     {
12359         Roo.log('onTogglePeriod');
12360         this.time = this.time.add(Date.HOUR, 12);
12361         this.update();
12362     }
12363     
12364    
12365 });
12366
12367 Roo.apply(Roo.bootstrap.TimeField,  {
12368     
12369     content : {
12370         tag: 'tbody',
12371         cn: [
12372             {
12373                 tag: 'tr',
12374                 cn: [
12375                 {
12376                     tag: 'td',
12377                     colspan: '7'
12378                 }
12379                 ]
12380             }
12381         ]
12382     },
12383     
12384     footer : {
12385         tag: 'tfoot',
12386         cn: [
12387             {
12388                 tag: 'tr',
12389                 cn: [
12390                 {
12391                     tag: 'th',
12392                     colspan: '7',
12393                     cls: '',
12394                     cn: [
12395                         {
12396                             tag: 'button',
12397                             cls: 'btn btn-info ok',
12398                             html: 'OK'
12399                         }
12400                     ]
12401                 }
12402
12403                 ]
12404             }
12405         ]
12406     }
12407 });
12408
12409 Roo.apply(Roo.bootstrap.TimeField,  {
12410   
12411     template : {
12412         tag: 'div',
12413         cls: 'datepicker dropdown-menu',
12414         cn: [
12415             {
12416                 tag: 'div',
12417                 cls: 'datepicker-time',
12418                 cn: [
12419                 {
12420                     tag: 'table',
12421                     cls: 'table-condensed',
12422                     cn:[
12423                     Roo.bootstrap.TimeField.content,
12424                     Roo.bootstrap.TimeField.footer
12425                     ]
12426                 }
12427                 ]
12428             }
12429         ]
12430     }
12431 });
12432
12433  
12434
12435  /*
12436  * - LGPL
12437  *
12438  * CheckBox
12439  * 
12440  */
12441
12442 /**
12443  * @class Roo.bootstrap.CheckBox
12444  * @extends Roo.bootstrap.Input
12445  * Bootstrap CheckBox class
12446  * 
12447  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12448  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12449  * @cfg {String} boxLabel The text that appears beside the checkbox
12450  * @cfg {Boolean} checked initnal the element
12451  * 
12452  * @constructor
12453  * Create a new CheckBox
12454  * @param {Object} config The config object
12455  */
12456
12457 Roo.bootstrap.CheckBox = function(config){
12458     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12459    
12460         this.addEvents({
12461             /**
12462             * @event check
12463             * Fires when the element is checked or unchecked.
12464             * @param {Roo.bootstrap.CheckBox} this This input
12465             * @param {Boolean} checked The new checked value
12466             */
12467            check : true
12468         });
12469 };
12470
12471 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12472     
12473     inputType: 'checkbox',
12474     inputValue: 1,
12475     valueOff: 0,
12476     boxLabel: false,
12477     checked: false,
12478     
12479     getAutoCreate : function()
12480     {
12481         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12482         
12483         var id = Roo.id();
12484         
12485         var cfg = {};
12486         
12487         cfg.cls = 'form-group' //input-group
12488         
12489         var input =  {
12490             tag: 'input',
12491             id : id,
12492             type : this.inputType,
12493             value : (!this.checked) ? this.valueOff : this.inputValue,
12494             cls : 'form-box',
12495             placeholder : this.placeholder || ''
12496             
12497         };
12498         
12499         if (this.disabled) {
12500             input.disabled=true;
12501         }
12502         
12503         if(this.checked){
12504             input.checked = this.checked;
12505         }
12506         
12507         if (this.name) {
12508             input.name = this.name;
12509         }
12510         
12511         if (this.size) {
12512             input.cls += ' input-' + this.size;
12513         }
12514         
12515         var settings=this;
12516         ['xs','sm','md','lg'].map(function(size){
12517             if (settings[size]) {
12518                 cfg.cls += ' col-' + size + '-' + settings[size];
12519             }
12520         });
12521         
12522         var inputblock = input;
12523         
12524         if (this.before || this.after) {
12525             
12526             inputblock = {
12527                 cls : 'input-group',
12528                 cn :  [] 
12529             };
12530             if (this.before) {
12531                 inputblock.cn.push({
12532                     tag :'span',
12533                     cls : 'input-group-addon',
12534                     html : this.before
12535                 });
12536             }
12537             inputblock.cn.push(input);
12538             if (this.after) {
12539                 inputblock.cn.push({
12540                     tag :'span',
12541                     cls : 'input-group-addon',
12542                     html : this.after
12543                 });
12544             }
12545             
12546         };
12547         
12548         if (align ==='left' && this.fieldLabel.length) {
12549                 Roo.log("left and has label");
12550                 cfg.cn = [
12551                     
12552                     {
12553                         tag: 'label',
12554                         'for' :  id,
12555                         cls : 'control-label col-md-' + this.labelWidth,
12556                         html : this.fieldLabel
12557                         
12558                     },
12559                     {
12560                         cls : "col-md-" + (12 - this.labelWidth), 
12561                         cn: [
12562                             inputblock
12563                         ]
12564                     }
12565                     
12566                 ];
12567         } else if ( this.fieldLabel.length) {
12568                 Roo.log(" label");
12569                 cfg.cn = [
12570                    
12571                     {
12572                         tag: this.boxLabel ? 'span' : 'label',
12573                         'for': id,
12574                         cls: 'control-label box-input-label',
12575                         //cls : 'input-group-addon',
12576                         html : this.fieldLabel
12577                         
12578                     },
12579                     
12580                     inputblock
12581                     
12582                 ];
12583
12584         } else {
12585             
12586                    Roo.log(" no label && no align");
12587                 cfg.cn = [
12588                     
12589                         inputblock
12590                     
12591                 ];
12592                 
12593                 
12594         };
12595         
12596         if(this.boxLabel){
12597             cfg.cn.push({
12598                 tag: 'label',
12599                 'for': id,
12600                 cls: 'box-label',
12601                 html: this.boxLabel
12602             })
12603         }
12604         
12605         return cfg;
12606         
12607     },
12608     
12609     /**
12610      * return the real input element.
12611      */
12612     inputEl: function ()
12613     {
12614         return this.el.select('input.form-box',true).first();
12615     },
12616     
12617     initEvents : function()
12618     {
12619 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12620         
12621         this.inputEl().on('click', this.onClick,  this);
12622         
12623     },
12624     
12625     onClick : function()
12626     {   
12627         this.setChecked(!this.checked);
12628     },
12629     
12630     setChecked : function(state,suppressEvent)
12631     {
12632         this.checked = state;
12633         
12634         this.inputEl().dom.checked = state;
12635         
12636         if(suppressEvent !== true){
12637             this.fireEvent('check', this, state);
12638         }
12639         
12640         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12641         
12642     },
12643     
12644     setValue : function(v,suppressEvent)
12645     {
12646         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12647     }
12648     
12649 });
12650
12651  
12652 /*
12653  * - LGPL
12654  *
12655  * Radio
12656  * 
12657  */
12658
12659 /**
12660  * @class Roo.bootstrap.Radio
12661  * @extends Roo.bootstrap.CheckBox
12662  * Bootstrap Radio class
12663
12664  * @constructor
12665  * Create a new Radio
12666  * @param {Object} config The config object
12667  */
12668
12669 Roo.bootstrap.Radio = function(config){
12670     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12671    
12672 };
12673
12674 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12675     
12676     inputType: 'radio',
12677     inputValue: '',
12678     valueOff: '',
12679     
12680     getAutoCreate : function()
12681     {
12682         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12683         
12684         var id = Roo.id();
12685         
12686         var cfg = {};
12687         
12688         cfg.cls = 'form-group' //input-group
12689         
12690         var input =  {
12691             tag: 'input',
12692             id : id,
12693             type : this.inputType,
12694             value : (!this.checked) ? this.valueOff : this.inputValue,
12695             cls : 'form-box',
12696             placeholder : this.placeholder || ''
12697             
12698         };
12699         
12700         if (this.disabled) {
12701             input.disabled=true;
12702         }
12703         
12704         if(this.checked){
12705             input.checked = this.checked;
12706         }
12707         
12708         if (this.name) {
12709             input.name = this.name;
12710         }
12711         
12712         if (this.size) {
12713             input.cls += ' input-' + this.size;
12714         }
12715         
12716         var settings=this;
12717         ['xs','sm','md','lg'].map(function(size){
12718             if (settings[size]) {
12719                 cfg.cls += ' col-' + size + '-' + settings[size];
12720             }
12721         });
12722         
12723         var inputblock = input;
12724         
12725         if (this.before || this.after) {
12726             
12727             inputblock = {
12728                 cls : 'input-group',
12729                 cn :  [] 
12730             };
12731             if (this.before) {
12732                 inputblock.cn.push({
12733                     tag :'span',
12734                     cls : 'input-group-addon',
12735                     html : this.before
12736                 });
12737             }
12738             inputblock.cn.push(input);
12739             if (this.after) {
12740                 inputblock.cn.push({
12741                     tag :'span',
12742                     cls : 'input-group-addon',
12743                     html : this.after
12744                 });
12745             }
12746             
12747         };
12748         
12749         if (align ==='left' && this.fieldLabel.length) {
12750                 Roo.log("left and has label");
12751                 cfg.cn = [
12752                     
12753                     {
12754                         tag: 'label',
12755                         'for' :  id,
12756                         cls : 'control-label col-md-' + this.labelWidth,
12757                         html : this.fieldLabel
12758                         
12759                     },
12760                     {
12761                         cls : "col-md-" + (12 - this.labelWidth), 
12762                         cn: [
12763                             inputblock
12764                         ]
12765                     }
12766                     
12767                 ];
12768         } else if ( this.fieldLabel.length) {
12769                 Roo.log(" label");
12770                  cfg.cn = [
12771                    
12772                     {
12773                         tag: 'label',
12774                         'for': id,
12775                         cls: 'control-label box-input-label',
12776                         //cls : 'input-group-addon',
12777                         html : this.fieldLabel
12778                         
12779                     },
12780                     
12781                     inputblock
12782                     
12783                 ];
12784
12785         } else {
12786             
12787                    Roo.log(" no label && no align");
12788                 cfg.cn = [
12789                     
12790                         inputblock
12791                     
12792                 ];
12793                 
12794                 
12795         };
12796         
12797         if(this.boxLabel){
12798             cfg.cn.push({
12799                 tag: 'label',
12800                 'for': id,
12801                 cls: 'box-label',
12802                 html: this.boxLabel
12803             })
12804         }
12805         
12806         return cfg;
12807         
12808     },
12809    
12810     onClick : function()
12811     {   
12812         this.setChecked(true);
12813     },
12814     
12815     setChecked : function(state,suppressEvent)
12816     {
12817         if(state){
12818             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12819                 v.dom.checked = false;
12820             });
12821         }
12822         
12823         this.checked = state;
12824         this.inputEl().dom.checked = state;
12825         
12826         if(suppressEvent !== true){
12827             this.fireEvent('check', this, state);
12828         }
12829         
12830         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12831         
12832     },
12833     
12834     getGroupValue : function()
12835     {
12836         var value = ''
12837         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12838             if(v.dom.checked == true){
12839                 value = v.dom.value;
12840             }
12841         });
12842         
12843         return value;
12844     },
12845     
12846     /**
12847      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12848      * @return {Mixed} value The field value
12849      */
12850     getValue : function(){
12851         return this.getGroupValue();
12852     }
12853     
12854 });
12855
12856  
12857 //<script type="text/javascript">
12858
12859 /*
12860  * Based  Ext JS Library 1.1.1
12861  * Copyright(c) 2006-2007, Ext JS, LLC.
12862  * LGPL
12863  *
12864  */
12865  
12866 /**
12867  * @class Roo.HtmlEditorCore
12868  * @extends Roo.Component
12869  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12870  *
12871  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12872  */
12873
12874 Roo.HtmlEditorCore = function(config){
12875     
12876     
12877     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12878     this.addEvents({
12879         /**
12880          * @event initialize
12881          * Fires when the editor is fully initialized (including the iframe)
12882          * @param {Roo.HtmlEditorCore} this
12883          */
12884         initialize: true,
12885         /**
12886          * @event activate
12887          * Fires when the editor is first receives the focus. Any insertion must wait
12888          * until after this event.
12889          * @param {Roo.HtmlEditorCore} this
12890          */
12891         activate: true,
12892          /**
12893          * @event beforesync
12894          * Fires before the textarea is updated with content from the editor iframe. Return false
12895          * to cancel the sync.
12896          * @param {Roo.HtmlEditorCore} this
12897          * @param {String} html
12898          */
12899         beforesync: true,
12900          /**
12901          * @event beforepush
12902          * Fires before the iframe editor is updated with content from the textarea. Return false
12903          * to cancel the push.
12904          * @param {Roo.HtmlEditorCore} this
12905          * @param {String} html
12906          */
12907         beforepush: true,
12908          /**
12909          * @event sync
12910          * Fires when the textarea is updated with content from the editor iframe.
12911          * @param {Roo.HtmlEditorCore} this
12912          * @param {String} html
12913          */
12914         sync: true,
12915          /**
12916          * @event push
12917          * Fires when the iframe editor is updated with content from the textarea.
12918          * @param {Roo.HtmlEditorCore} this
12919          * @param {String} html
12920          */
12921         push: true,
12922         
12923         /**
12924          * @event editorevent
12925          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12926          * @param {Roo.HtmlEditorCore} this
12927          */
12928         editorevent: true
12929     });
12930      
12931 };
12932
12933
12934 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12935
12936
12937      /**
12938      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12939      */
12940     
12941     owner : false,
12942     
12943      /**
12944      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12945      *                        Roo.resizable.
12946      */
12947     resizable : false,
12948      /**
12949      * @cfg {Number} height (in pixels)
12950      */   
12951     height: 300,
12952    /**
12953      * @cfg {Number} width (in pixels)
12954      */   
12955     width: 500,
12956     
12957     /**
12958      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12959      * 
12960      */
12961     stylesheets: false,
12962     
12963     // id of frame..
12964     frameId: false,
12965     
12966     // private properties
12967     validationEvent : false,
12968     deferHeight: true,
12969     initialized : false,
12970     activated : false,
12971     sourceEditMode : false,
12972     onFocus : Roo.emptyFn,
12973     iframePad:3,
12974     hideMode:'offsets',
12975     
12976     clearUp: true,
12977     
12978      
12979     
12980
12981     /**
12982      * Protected method that will not generally be called directly. It
12983      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12984      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12985      */
12986     getDocMarkup : function(){
12987         // body styles..
12988         var st = '';
12989         Roo.log(this.stylesheets);
12990         
12991         // inherit styels from page...?? 
12992         if (this.stylesheets === false) {
12993             
12994             Roo.get(document.head).select('style').each(function(node) {
12995                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12996             });
12997             
12998             Roo.get(document.head).select('link').each(function(node) { 
12999                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13000             });
13001             
13002         } else if (!this.stylesheets.length) {
13003                 // simple..
13004                 st = '<style type="text/css">' +
13005                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13006                    '</style>';
13007         } else {
13008             Roo.each(this.stylesheets, function(s) {
13009                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13010             });
13011             
13012         }
13013         
13014         st +=  '<style type="text/css">' +
13015             'IMG { cursor: pointer } ' +
13016         '</style>';
13017
13018         
13019         return '<html><head>' + st  +
13020             //<style type="text/css">' +
13021             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13022             //'</style>' +
13023             ' </head><body class="roo-htmleditor-body"></body></html>';
13024     },
13025
13026     // private
13027     onRender : function(ct, position)
13028     {
13029         var _t = this;
13030         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13031         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13032         
13033         
13034         this.el.dom.style.border = '0 none';
13035         this.el.dom.setAttribute('tabIndex', -1);
13036         this.el.addClass('x-hidden hide');
13037         
13038         
13039         
13040         if(Roo.isIE){ // fix IE 1px bogus margin
13041             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13042         }
13043        
13044         
13045         this.frameId = Roo.id();
13046         
13047          
13048         
13049         var iframe = this.owner.wrap.createChild({
13050             tag: 'iframe',
13051             cls: 'form-control', // bootstrap..
13052             id: this.frameId,
13053             name: this.frameId,
13054             frameBorder : 'no',
13055             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13056         }, this.el
13057         );
13058         
13059         
13060         this.iframe = iframe.dom;
13061
13062          this.assignDocWin();
13063         
13064         this.doc.designMode = 'on';
13065        
13066         this.doc.open();
13067         this.doc.write(this.getDocMarkup());
13068         this.doc.close();
13069
13070         
13071         var task = { // must defer to wait for browser to be ready
13072             run : function(){
13073                 //console.log("run task?" + this.doc.readyState);
13074                 this.assignDocWin();
13075                 if(this.doc.body || this.doc.readyState == 'complete'){
13076                     try {
13077                         this.doc.designMode="on";
13078                     } catch (e) {
13079                         return;
13080                     }
13081                     Roo.TaskMgr.stop(task);
13082                     this.initEditor.defer(10, this);
13083                 }
13084             },
13085             interval : 10,
13086             duration: 10000,
13087             scope: this
13088         };
13089         Roo.TaskMgr.start(task);
13090
13091         
13092          
13093     },
13094
13095     // private
13096     onResize : function(w, h)
13097     {
13098          Roo.log('resize: ' +w + ',' + h );
13099         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13100         if(!this.iframe){
13101             return;
13102         }
13103         if(typeof w == 'number'){
13104             
13105             this.iframe.style.width = w + 'px';
13106         }
13107         if(typeof h == 'number'){
13108             
13109             this.iframe.style.height = h + 'px';
13110             if(this.doc){
13111                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13112             }
13113         }
13114         
13115     },
13116
13117     /**
13118      * Toggles the editor between standard and source edit mode.
13119      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13120      */
13121     toggleSourceEdit : function(sourceEditMode){
13122         
13123         this.sourceEditMode = sourceEditMode === true;
13124         
13125         if(this.sourceEditMode){
13126  
13127             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13128             
13129         }else{
13130             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13131             //this.iframe.className = '';
13132             this.deferFocus();
13133         }
13134         //this.setSize(this.owner.wrap.getSize());
13135         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13136     },
13137
13138     
13139   
13140
13141     /**
13142      * Protected method that will not generally be called directly. If you need/want
13143      * custom HTML cleanup, this is the method you should override.
13144      * @param {String} html The HTML to be cleaned
13145      * return {String} The cleaned HTML
13146      */
13147     cleanHtml : function(html){
13148         html = String(html);
13149         if(html.length > 5){
13150             if(Roo.isSafari){ // strip safari nonsense
13151                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13152             }
13153         }
13154         if(html == '&nbsp;'){
13155             html = '';
13156         }
13157         return html;
13158     },
13159
13160     /**
13161      * HTML Editor -> Textarea
13162      * Protected method that will not generally be called directly. Syncs the contents
13163      * of the editor iframe with the textarea.
13164      */
13165     syncValue : function(){
13166         if(this.initialized){
13167             var bd = (this.doc.body || this.doc.documentElement);
13168             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13169             var html = bd.innerHTML;
13170             if(Roo.isSafari){
13171                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13172                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13173                 if(m && m[1]){
13174                     html = '<div style="'+m[0]+'">' + html + '</div>';
13175                 }
13176             }
13177             html = this.cleanHtml(html);
13178             // fix up the special chars.. normaly like back quotes in word...
13179             // however we do not want to do this with chinese..
13180             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13181                 var cc = b.charCodeAt();
13182                 if (
13183                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13184                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13185                     (cc >= 0xf900 && cc < 0xfb00 )
13186                 ) {
13187                         return b;
13188                 }
13189                 return "&#"+cc+";" 
13190             });
13191             if(this.owner.fireEvent('beforesync', this, html) !== false){
13192                 this.el.dom.value = html;
13193                 this.owner.fireEvent('sync', this, html);
13194             }
13195         }
13196     },
13197
13198     /**
13199      * Protected method that will not generally be called directly. Pushes the value of the textarea
13200      * into the iframe editor.
13201      */
13202     pushValue : function(){
13203         if(this.initialized){
13204             var v = this.el.dom.value.trim();
13205             
13206 //            if(v.length < 1){
13207 //                v = '&#160;';
13208 //            }
13209             
13210             if(this.owner.fireEvent('beforepush', this, v) !== false){
13211                 var d = (this.doc.body || this.doc.documentElement);
13212                 d.innerHTML = v;
13213                 this.cleanUpPaste();
13214                 this.el.dom.value = d.innerHTML;
13215                 this.owner.fireEvent('push', this, v);
13216             }
13217         }
13218     },
13219
13220     // private
13221     deferFocus : function(){
13222         this.focus.defer(10, this);
13223     },
13224
13225     // doc'ed in Field
13226     focus : function(){
13227         if(this.win && !this.sourceEditMode){
13228             this.win.focus();
13229         }else{
13230             this.el.focus();
13231         }
13232     },
13233     
13234     assignDocWin: function()
13235     {
13236         var iframe = this.iframe;
13237         
13238          if(Roo.isIE){
13239             this.doc = iframe.contentWindow.document;
13240             this.win = iframe.contentWindow;
13241         } else {
13242             if (!Roo.get(this.frameId)) {
13243                 return;
13244             }
13245             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13246             this.win = Roo.get(this.frameId).dom.contentWindow;
13247         }
13248     },
13249     
13250     // private
13251     initEditor : function(){
13252         //console.log("INIT EDITOR");
13253         this.assignDocWin();
13254         
13255         
13256         
13257         this.doc.designMode="on";
13258         this.doc.open();
13259         this.doc.write(this.getDocMarkup());
13260         this.doc.close();
13261         
13262         var dbody = (this.doc.body || this.doc.documentElement);
13263         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13264         // this copies styles from the containing element into thsi one..
13265         // not sure why we need all of this..
13266         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13267         ss['background-attachment'] = 'fixed'; // w3c
13268         dbody.bgProperties = 'fixed'; // ie
13269         Roo.DomHelper.applyStyles(dbody, ss);
13270         Roo.EventManager.on(this.doc, {
13271             //'mousedown': this.onEditorEvent,
13272             'mouseup': this.onEditorEvent,
13273             'dblclick': this.onEditorEvent,
13274             'click': this.onEditorEvent,
13275             'keyup': this.onEditorEvent,
13276             buffer:100,
13277             scope: this
13278         });
13279         if(Roo.isGecko){
13280             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13281         }
13282         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13283             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13284         }
13285         this.initialized = true;
13286
13287         this.owner.fireEvent('initialize', this);
13288         this.pushValue();
13289     },
13290
13291     // private
13292     onDestroy : function(){
13293         
13294         
13295         
13296         if(this.rendered){
13297             
13298             //for (var i =0; i < this.toolbars.length;i++) {
13299             //    // fixme - ask toolbars for heights?
13300             //    this.toolbars[i].onDestroy();
13301            // }
13302             
13303             //this.wrap.dom.innerHTML = '';
13304             //this.wrap.remove();
13305         }
13306     },
13307
13308     // private
13309     onFirstFocus : function(){
13310         
13311         this.assignDocWin();
13312         
13313         
13314         this.activated = true;
13315          
13316     
13317         if(Roo.isGecko){ // prevent silly gecko errors
13318             this.win.focus();
13319             var s = this.win.getSelection();
13320             if(!s.focusNode || s.focusNode.nodeType != 3){
13321                 var r = s.getRangeAt(0);
13322                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13323                 r.collapse(true);
13324                 this.deferFocus();
13325             }
13326             try{
13327                 this.execCmd('useCSS', true);
13328                 this.execCmd('styleWithCSS', false);
13329             }catch(e){}
13330         }
13331         this.owner.fireEvent('activate', this);
13332     },
13333
13334     // private
13335     adjustFont: function(btn){
13336         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13337         //if(Roo.isSafari){ // safari
13338         //    adjust *= 2;
13339        // }
13340         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13341         if(Roo.isSafari){ // safari
13342             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13343             v =  (v < 10) ? 10 : v;
13344             v =  (v > 48) ? 48 : v;
13345             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13346             
13347         }
13348         
13349         
13350         v = Math.max(1, v+adjust);
13351         
13352         this.execCmd('FontSize', v  );
13353     },
13354
13355     onEditorEvent : function(e){
13356         this.owner.fireEvent('editorevent', this, e);
13357       //  this.updateToolbar();
13358         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13359     },
13360
13361     insertTag : function(tg)
13362     {
13363         // could be a bit smarter... -> wrap the current selected tRoo..
13364         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13365             
13366             range = this.createRange(this.getSelection());
13367             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13368             wrappingNode.appendChild(range.extractContents());
13369             range.insertNode(wrappingNode);
13370
13371             return;
13372             
13373             
13374             
13375         }
13376         this.execCmd("formatblock",   tg);
13377         
13378     },
13379     
13380     insertText : function(txt)
13381     {
13382         
13383         
13384         var range = this.createRange();
13385         range.deleteContents();
13386                //alert(Sender.getAttribute('label'));
13387                
13388         range.insertNode(this.doc.createTextNode(txt));
13389     } ,
13390     
13391      
13392
13393     /**
13394      * Executes a Midas editor command on the editor document and performs necessary focus and
13395      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13396      * @param {String} cmd The Midas command
13397      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13398      */
13399     relayCmd : function(cmd, value){
13400         this.win.focus();
13401         this.execCmd(cmd, value);
13402         this.owner.fireEvent('editorevent', this);
13403         //this.updateToolbar();
13404         this.owner.deferFocus();
13405     },
13406
13407     /**
13408      * Executes a Midas editor command directly on the editor document.
13409      * For visual commands, you should use {@link #relayCmd} instead.
13410      * <b>This should only be called after the editor is initialized.</b>
13411      * @param {String} cmd The Midas command
13412      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13413      */
13414     execCmd : function(cmd, value){
13415         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13416         this.syncValue();
13417     },
13418  
13419  
13420    
13421     /**
13422      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13423      * to insert tRoo.
13424      * @param {String} text | dom node.. 
13425      */
13426     insertAtCursor : function(text)
13427     {
13428         
13429         
13430         
13431         if(!this.activated){
13432             return;
13433         }
13434         /*
13435         if(Roo.isIE){
13436             this.win.focus();
13437             var r = this.doc.selection.createRange();
13438             if(r){
13439                 r.collapse(true);
13440                 r.pasteHTML(text);
13441                 this.syncValue();
13442                 this.deferFocus();
13443             
13444             }
13445             return;
13446         }
13447         */
13448         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13449             this.win.focus();
13450             
13451             
13452             // from jquery ui (MIT licenced)
13453             var range, node;
13454             var win = this.win;
13455             
13456             if (win.getSelection && win.getSelection().getRangeAt) {
13457                 range = win.getSelection().getRangeAt(0);
13458                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13459                 range.insertNode(node);
13460             } else if (win.document.selection && win.document.selection.createRange) {
13461                 // no firefox support
13462                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13463                 win.document.selection.createRange().pasteHTML(txt);
13464             } else {
13465                 // no firefox support
13466                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13467                 this.execCmd('InsertHTML', txt);
13468             } 
13469             
13470             this.syncValue();
13471             
13472             this.deferFocus();
13473         }
13474     },
13475  // private
13476     mozKeyPress : function(e){
13477         if(e.ctrlKey){
13478             var c = e.getCharCode(), cmd;
13479           
13480             if(c > 0){
13481                 c = String.fromCharCode(c).toLowerCase();
13482                 switch(c){
13483                     case 'b':
13484                         cmd = 'bold';
13485                         break;
13486                     case 'i':
13487                         cmd = 'italic';
13488                         break;
13489                     
13490                     case 'u':
13491                         cmd = 'underline';
13492                         break;
13493                     
13494                     case 'v':
13495                         this.cleanUpPaste.defer(100, this);
13496                         return;
13497                         
13498                 }
13499                 if(cmd){
13500                     this.win.focus();
13501                     this.execCmd(cmd);
13502                     this.deferFocus();
13503                     e.preventDefault();
13504                 }
13505                 
13506             }
13507         }
13508     },
13509
13510     // private
13511     fixKeys : function(){ // load time branching for fastest keydown performance
13512         if(Roo.isIE){
13513             return function(e){
13514                 var k = e.getKey(), r;
13515                 if(k == e.TAB){
13516                     e.stopEvent();
13517                     r = this.doc.selection.createRange();
13518                     if(r){
13519                         r.collapse(true);
13520                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13521                         this.deferFocus();
13522                     }
13523                     return;
13524                 }
13525                 
13526                 if(k == e.ENTER){
13527                     r = this.doc.selection.createRange();
13528                     if(r){
13529                         var target = r.parentElement();
13530                         if(!target || target.tagName.toLowerCase() != 'li'){
13531                             e.stopEvent();
13532                             r.pasteHTML('<br />');
13533                             r.collapse(false);
13534                             r.select();
13535                         }
13536                     }
13537                 }
13538                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13539                     this.cleanUpPaste.defer(100, this);
13540                     return;
13541                 }
13542                 
13543                 
13544             };
13545         }else if(Roo.isOpera){
13546             return function(e){
13547                 var k = e.getKey();
13548                 if(k == e.TAB){
13549                     e.stopEvent();
13550                     this.win.focus();
13551                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13552                     this.deferFocus();
13553                 }
13554                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13555                     this.cleanUpPaste.defer(100, this);
13556                     return;
13557                 }
13558                 
13559             };
13560         }else if(Roo.isSafari){
13561             return function(e){
13562                 var k = e.getKey();
13563                 
13564                 if(k == e.TAB){
13565                     e.stopEvent();
13566                     this.execCmd('InsertText','\t');
13567                     this.deferFocus();
13568                     return;
13569                 }
13570                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13571                     this.cleanUpPaste.defer(100, this);
13572                     return;
13573                 }
13574                 
13575              };
13576         }
13577     }(),
13578     
13579     getAllAncestors: function()
13580     {
13581         var p = this.getSelectedNode();
13582         var a = [];
13583         if (!p) {
13584             a.push(p); // push blank onto stack..
13585             p = this.getParentElement();
13586         }
13587         
13588         
13589         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13590             a.push(p);
13591             p = p.parentNode;
13592         }
13593         a.push(this.doc.body);
13594         return a;
13595     },
13596     lastSel : false,
13597     lastSelNode : false,
13598     
13599     
13600     getSelection : function() 
13601     {
13602         this.assignDocWin();
13603         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13604     },
13605     
13606     getSelectedNode: function() 
13607     {
13608         // this may only work on Gecko!!!
13609         
13610         // should we cache this!!!!
13611         
13612         
13613         
13614          
13615         var range = this.createRange(this.getSelection()).cloneRange();
13616         
13617         if (Roo.isIE) {
13618             var parent = range.parentElement();
13619             while (true) {
13620                 var testRange = range.duplicate();
13621                 testRange.moveToElementText(parent);
13622                 if (testRange.inRange(range)) {
13623                     break;
13624                 }
13625                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13626                     break;
13627                 }
13628                 parent = parent.parentElement;
13629             }
13630             return parent;
13631         }
13632         
13633         // is ancestor a text element.
13634         var ac =  range.commonAncestorContainer;
13635         if (ac.nodeType == 3) {
13636             ac = ac.parentNode;
13637         }
13638         
13639         var ar = ac.childNodes;
13640          
13641         var nodes = [];
13642         var other_nodes = [];
13643         var has_other_nodes = false;
13644         for (var i=0;i<ar.length;i++) {
13645             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13646                 continue;
13647             }
13648             // fullly contained node.
13649             
13650             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13651                 nodes.push(ar[i]);
13652                 continue;
13653             }
13654             
13655             // probably selected..
13656             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13657                 other_nodes.push(ar[i]);
13658                 continue;
13659             }
13660             // outer..
13661             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13662                 continue;
13663             }
13664             
13665             
13666             has_other_nodes = true;
13667         }
13668         if (!nodes.length && other_nodes.length) {
13669             nodes= other_nodes;
13670         }
13671         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13672             return false;
13673         }
13674         
13675         return nodes[0];
13676     },
13677     createRange: function(sel)
13678     {
13679         // this has strange effects when using with 
13680         // top toolbar - not sure if it's a great idea.
13681         //this.editor.contentWindow.focus();
13682         if (typeof sel != "undefined") {
13683             try {
13684                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13685             } catch(e) {
13686                 return this.doc.createRange();
13687             }
13688         } else {
13689             return this.doc.createRange();
13690         }
13691     },
13692     getParentElement: function()
13693     {
13694         
13695         this.assignDocWin();
13696         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13697         
13698         var range = this.createRange(sel);
13699          
13700         try {
13701             var p = range.commonAncestorContainer;
13702             while (p.nodeType == 3) { // text node
13703                 p = p.parentNode;
13704             }
13705             return p;
13706         } catch (e) {
13707             return null;
13708         }
13709     
13710     },
13711     /***
13712      *
13713      * Range intersection.. the hard stuff...
13714      *  '-1' = before
13715      *  '0' = hits..
13716      *  '1' = after.
13717      *         [ -- selected range --- ]
13718      *   [fail]                        [fail]
13719      *
13720      *    basically..
13721      *      if end is before start or  hits it. fail.
13722      *      if start is after end or hits it fail.
13723      *
13724      *   if either hits (but other is outside. - then it's not 
13725      *   
13726      *    
13727      **/
13728     
13729     
13730     // @see http://www.thismuchiknow.co.uk/?p=64.
13731     rangeIntersectsNode : function(range, node)
13732     {
13733         var nodeRange = node.ownerDocument.createRange();
13734         try {
13735             nodeRange.selectNode(node);
13736         } catch (e) {
13737             nodeRange.selectNodeContents(node);
13738         }
13739     
13740         var rangeStartRange = range.cloneRange();
13741         rangeStartRange.collapse(true);
13742     
13743         var rangeEndRange = range.cloneRange();
13744         rangeEndRange.collapse(false);
13745     
13746         var nodeStartRange = nodeRange.cloneRange();
13747         nodeStartRange.collapse(true);
13748     
13749         var nodeEndRange = nodeRange.cloneRange();
13750         nodeEndRange.collapse(false);
13751     
13752         return rangeStartRange.compareBoundaryPoints(
13753                  Range.START_TO_START, nodeEndRange) == -1 &&
13754                rangeEndRange.compareBoundaryPoints(
13755                  Range.START_TO_START, nodeStartRange) == 1;
13756         
13757          
13758     },
13759     rangeCompareNode : function(range, node)
13760     {
13761         var nodeRange = node.ownerDocument.createRange();
13762         try {
13763             nodeRange.selectNode(node);
13764         } catch (e) {
13765             nodeRange.selectNodeContents(node);
13766         }
13767         
13768         
13769         range.collapse(true);
13770     
13771         nodeRange.collapse(true);
13772      
13773         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13774         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13775          
13776         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13777         
13778         var nodeIsBefore   =  ss == 1;
13779         var nodeIsAfter    = ee == -1;
13780         
13781         if (nodeIsBefore && nodeIsAfter)
13782             return 0; // outer
13783         if (!nodeIsBefore && nodeIsAfter)
13784             return 1; //right trailed.
13785         
13786         if (nodeIsBefore && !nodeIsAfter)
13787             return 2;  // left trailed.
13788         // fully contined.
13789         return 3;
13790     },
13791
13792     // private? - in a new class?
13793     cleanUpPaste :  function()
13794     {
13795         // cleans up the whole document..
13796         Roo.log('cleanuppaste');
13797         
13798         this.cleanUpChildren(this.doc.body);
13799         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13800         if (clean != this.doc.body.innerHTML) {
13801             this.doc.body.innerHTML = clean;
13802         }
13803         
13804     },
13805     
13806     cleanWordChars : function(input) {// change the chars to hex code
13807         var he = Roo.HtmlEditorCore;
13808         
13809         var output = input;
13810         Roo.each(he.swapCodes, function(sw) { 
13811             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13812             
13813             output = output.replace(swapper, sw[1]);
13814         });
13815         
13816         return output;
13817     },
13818     
13819     
13820     cleanUpChildren : function (n)
13821     {
13822         if (!n.childNodes.length) {
13823             return;
13824         }
13825         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13826            this.cleanUpChild(n.childNodes[i]);
13827         }
13828     },
13829     
13830     
13831         
13832     
13833     cleanUpChild : function (node)
13834     {
13835         var ed = this;
13836         //console.log(node);
13837         if (node.nodeName == "#text") {
13838             // clean up silly Windows -- stuff?
13839             return; 
13840         }
13841         if (node.nodeName == "#comment") {
13842             node.parentNode.removeChild(node);
13843             // clean up silly Windows -- stuff?
13844             return; 
13845         }
13846         
13847         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13848             // remove node.
13849             node.parentNode.removeChild(node);
13850             return;
13851             
13852         }
13853         
13854         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13855         
13856         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13857         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13858         
13859         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13860         //    remove_keep_children = true;
13861         //}
13862         
13863         if (remove_keep_children) {
13864             this.cleanUpChildren(node);
13865             // inserts everything just before this node...
13866             while (node.childNodes.length) {
13867                 var cn = node.childNodes[0];
13868                 node.removeChild(cn);
13869                 node.parentNode.insertBefore(cn, node);
13870             }
13871             node.parentNode.removeChild(node);
13872             return;
13873         }
13874         
13875         if (!node.attributes || !node.attributes.length) {
13876             this.cleanUpChildren(node);
13877             return;
13878         }
13879         
13880         function cleanAttr(n,v)
13881         {
13882             
13883             if (v.match(/^\./) || v.match(/^\//)) {
13884                 return;
13885             }
13886             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13887                 return;
13888             }
13889             if (v.match(/^#/)) {
13890                 return;
13891             }
13892 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13893             node.removeAttribute(n);
13894             
13895         }
13896         
13897         function cleanStyle(n,v)
13898         {
13899             if (v.match(/expression/)) { //XSS?? should we even bother..
13900                 node.removeAttribute(n);
13901                 return;
13902             }
13903             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13904             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13905             
13906             
13907             var parts = v.split(/;/);
13908             var clean = [];
13909             
13910             Roo.each(parts, function(p) {
13911                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13912                 if (!p.length) {
13913                     return true;
13914                 }
13915                 var l = p.split(':').shift().replace(/\s+/g,'');
13916                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13917                 
13918                 if ( cblack.indexOf(l) > -1) {
13919 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13920                     //node.removeAttribute(n);
13921                     return true;
13922                 }
13923                 //Roo.log()
13924                 // only allow 'c whitelisted system attributes'
13925                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13926 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13927                     //node.removeAttribute(n);
13928                     return true;
13929                 }
13930                 
13931                 
13932                  
13933                 
13934                 clean.push(p);
13935                 return true;
13936             });
13937             if (clean.length) { 
13938                 node.setAttribute(n, clean.join(';'));
13939             } else {
13940                 node.removeAttribute(n);
13941             }
13942             
13943         }
13944         
13945         
13946         for (var i = node.attributes.length-1; i > -1 ; i--) {
13947             var a = node.attributes[i];
13948             //console.log(a);
13949             
13950             if (a.name.toLowerCase().substr(0,2)=='on')  {
13951                 node.removeAttribute(a.name);
13952                 continue;
13953             }
13954             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13955                 node.removeAttribute(a.name);
13956                 continue;
13957             }
13958             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13959                 cleanAttr(a.name,a.value); // fixme..
13960                 continue;
13961             }
13962             if (a.name == 'style') {
13963                 cleanStyle(a.name,a.value);
13964                 continue;
13965             }
13966             /// clean up MS crap..
13967             // tecnically this should be a list of valid class'es..
13968             
13969             
13970             if (a.name == 'class') {
13971                 if (a.value.match(/^Mso/)) {
13972                     node.className = '';
13973                 }
13974                 
13975                 if (a.value.match(/body/)) {
13976                     node.className = '';
13977                 }
13978                 continue;
13979             }
13980             
13981             // style cleanup!?
13982             // class cleanup?
13983             
13984         }
13985         
13986         
13987         this.cleanUpChildren(node);
13988         
13989         
13990     }
13991     
13992     
13993     // hide stuff that is not compatible
13994     /**
13995      * @event blur
13996      * @hide
13997      */
13998     /**
13999      * @event change
14000      * @hide
14001      */
14002     /**
14003      * @event focus
14004      * @hide
14005      */
14006     /**
14007      * @event specialkey
14008      * @hide
14009      */
14010     /**
14011      * @cfg {String} fieldClass @hide
14012      */
14013     /**
14014      * @cfg {String} focusClass @hide
14015      */
14016     /**
14017      * @cfg {String} autoCreate @hide
14018      */
14019     /**
14020      * @cfg {String} inputType @hide
14021      */
14022     /**
14023      * @cfg {String} invalidClass @hide
14024      */
14025     /**
14026      * @cfg {String} invalidText @hide
14027      */
14028     /**
14029      * @cfg {String} msgFx @hide
14030      */
14031     /**
14032      * @cfg {String} validateOnBlur @hide
14033      */
14034 });
14035
14036 Roo.HtmlEditorCore.white = [
14037         'area', 'br', 'img', 'input', 'hr', 'wbr',
14038         
14039        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14040        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14041        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14042        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14043        'table',   'ul',         'xmp', 
14044        
14045        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14046       'thead',   'tr', 
14047      
14048       'dir', 'menu', 'ol', 'ul', 'dl',
14049        
14050       'embed',  'object'
14051 ];
14052
14053
14054 Roo.HtmlEditorCore.black = [
14055     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14056         'applet', // 
14057         'base',   'basefont', 'bgsound', 'blink',  'body', 
14058         'frame',  'frameset', 'head',    'html',   'ilayer', 
14059         'iframe', 'layer',  'link',     'meta',    'object',   
14060         'script', 'style' ,'title',  'xml' // clean later..
14061 ];
14062 Roo.HtmlEditorCore.clean = [
14063     'script', 'style', 'title', 'xml'
14064 ];
14065 Roo.HtmlEditorCore.remove = [
14066     'font'
14067 ];
14068 // attributes..
14069
14070 Roo.HtmlEditorCore.ablack = [
14071     'on'
14072 ];
14073     
14074 Roo.HtmlEditorCore.aclean = [ 
14075     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14076 ];
14077
14078 // protocols..
14079 Roo.HtmlEditorCore.pwhite= [
14080         'http',  'https',  'mailto'
14081 ];
14082
14083 // white listed style attributes.
14084 Roo.HtmlEditorCore.cwhite= [
14085       //  'text-align', /// default is to allow most things..
14086       
14087          
14088 //        'font-size'//??
14089 ];
14090
14091 // black listed style attributes.
14092 Roo.HtmlEditorCore.cblack= [
14093       //  'font-size' -- this can be set by the project 
14094 ];
14095
14096
14097 Roo.HtmlEditorCore.swapCodes   =[ 
14098     [    8211, "--" ], 
14099     [    8212, "--" ], 
14100     [    8216,  "'" ],  
14101     [    8217, "'" ],  
14102     [    8220, '"' ],  
14103     [    8221, '"' ],  
14104     [    8226, "*" ],  
14105     [    8230, "..." ]
14106 ]; 
14107
14108     /*
14109  * - LGPL
14110  *
14111  * HtmlEditor
14112  * 
14113  */
14114
14115 /**
14116  * @class Roo.bootstrap.HtmlEditor
14117  * @extends Roo.bootstrap.TextArea
14118  * Bootstrap HtmlEditor class
14119
14120  * @constructor
14121  * Create a new HtmlEditor
14122  * @param {Object} config The config object
14123  */
14124
14125 Roo.bootstrap.HtmlEditor = function(config){
14126     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14127     if (!this.toolbars) {
14128         this.toolbars = [];
14129     }
14130     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14131     this.addEvents({
14132             /**
14133              * @event initialize
14134              * Fires when the editor is fully initialized (including the iframe)
14135              * @param {HtmlEditor} this
14136              */
14137             initialize: true,
14138             /**
14139              * @event activate
14140              * Fires when the editor is first receives the focus. Any insertion must wait
14141              * until after this event.
14142              * @param {HtmlEditor} this
14143              */
14144             activate: true,
14145              /**
14146              * @event beforesync
14147              * Fires before the textarea is updated with content from the editor iframe. Return false
14148              * to cancel the sync.
14149              * @param {HtmlEditor} this
14150              * @param {String} html
14151              */
14152             beforesync: true,
14153              /**
14154              * @event beforepush
14155              * Fires before the iframe editor is updated with content from the textarea. Return false
14156              * to cancel the push.
14157              * @param {HtmlEditor} this
14158              * @param {String} html
14159              */
14160             beforepush: true,
14161              /**
14162              * @event sync
14163              * Fires when the textarea is updated with content from the editor iframe.
14164              * @param {HtmlEditor} this
14165              * @param {String} html
14166              */
14167             sync: true,
14168              /**
14169              * @event push
14170              * Fires when the iframe editor is updated with content from the textarea.
14171              * @param {HtmlEditor} this
14172              * @param {String} html
14173              */
14174             push: true,
14175              /**
14176              * @event editmodechange
14177              * Fires when the editor switches edit modes
14178              * @param {HtmlEditor} this
14179              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14180              */
14181             editmodechange: true,
14182             /**
14183              * @event editorevent
14184              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14185              * @param {HtmlEditor} this
14186              */
14187             editorevent: true,
14188             /**
14189              * @event firstfocus
14190              * Fires when on first focus - needed by toolbars..
14191              * @param {HtmlEditor} this
14192              */
14193             firstfocus: true,
14194             /**
14195              * @event autosave
14196              * Auto save the htmlEditor value as a file into Events
14197              * @param {HtmlEditor} this
14198              */
14199             autosave: true,
14200             /**
14201              * @event savedpreview
14202              * preview the saved version of htmlEditor
14203              * @param {HtmlEditor} this
14204              */
14205             savedpreview: true
14206         });
14207 };
14208
14209
14210 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14211     
14212     
14213       /**
14214      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14215      */
14216     toolbars : false,
14217    
14218      /**
14219      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14220      *                        Roo.resizable.
14221      */
14222     resizable : false,
14223      /**
14224      * @cfg {Number} height (in pixels)
14225      */   
14226     height: 300,
14227    /**
14228      * @cfg {Number} width (in pixels)
14229      */   
14230     width: false,
14231     
14232     /**
14233      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14234      * 
14235      */
14236     stylesheets: false,
14237     
14238     // id of frame..
14239     frameId: false,
14240     
14241     // private properties
14242     validationEvent : false,
14243     deferHeight: true,
14244     initialized : false,
14245     activated : false,
14246     
14247     onFocus : Roo.emptyFn,
14248     iframePad:3,
14249     hideMode:'offsets',
14250     
14251     
14252     tbContainer : false,
14253     
14254     toolbarContainer :function() {
14255         return this.wrap.select('.x-html-editor-tb',true).first();
14256     },
14257
14258     /**
14259      * Protected method that will not generally be called directly. It
14260      * is called when the editor creates its toolbar. Override this method if you need to
14261      * add custom toolbar buttons.
14262      * @param {HtmlEditor} editor
14263      */
14264     createToolbar : function(){
14265         
14266         Roo.log("create toolbars");
14267         
14268         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14269         this.toolbars[0].render(this.toolbarContainer());
14270         
14271         return;
14272         
14273 //        if (!editor.toolbars || !editor.toolbars.length) {
14274 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14275 //        }
14276 //        
14277 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14278 //            editor.toolbars[i] = Roo.factory(
14279 //                    typeof(editor.toolbars[i]) == 'string' ?
14280 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14281 //                Roo.bootstrap.HtmlEditor);
14282 //            editor.toolbars[i].init(editor);
14283 //        }
14284     },
14285
14286      
14287     // private
14288     onRender : function(ct, position)
14289     {
14290        // Roo.log("Call onRender: " + this.xtype);
14291         var _t = this;
14292         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14293       
14294         this.wrap = this.inputEl().wrap({
14295             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14296         });
14297         
14298         this.editorcore.onRender(ct, position);
14299          
14300         if (this.resizable) {
14301             this.resizeEl = new Roo.Resizable(this.wrap, {
14302                 pinned : true,
14303                 wrap: true,
14304                 dynamic : true,
14305                 minHeight : this.height,
14306                 height: this.height,
14307                 handles : this.resizable,
14308                 width: this.width,
14309                 listeners : {
14310                     resize : function(r, w, h) {
14311                         _t.onResize(w,h); // -something
14312                     }
14313                 }
14314             });
14315             
14316         }
14317         this.createToolbar(this);
14318        
14319         
14320         if(!this.width && this.resizable){
14321             this.setSize(this.wrap.getSize());
14322         }
14323         if (this.resizeEl) {
14324             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14325             // should trigger onReize..
14326         }
14327         
14328     },
14329
14330     // private
14331     onResize : function(w, h)
14332     {
14333         Roo.log('resize: ' +w + ',' + h );
14334         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14335         var ew = false;
14336         var eh = false;
14337         
14338         if(this.inputEl() ){
14339             if(typeof w == 'number'){
14340                 var aw = w - this.wrap.getFrameWidth('lr');
14341                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14342                 ew = aw;
14343             }
14344             if(typeof h == 'number'){
14345                  var tbh = -11;  // fixme it needs to tool bar size!
14346                 for (var i =0; i < this.toolbars.length;i++) {
14347                     // fixme - ask toolbars for heights?
14348                     tbh += this.toolbars[i].el.getHeight();
14349                     //if (this.toolbars[i].footer) {
14350                     //    tbh += this.toolbars[i].footer.el.getHeight();
14351                     //}
14352                 }
14353               
14354                 
14355                 
14356                 
14357                 
14358                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14359                 ah -= 5; // knock a few pixes off for look..
14360                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14361                 var eh = ah;
14362             }
14363         }
14364         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14365         this.editorcore.onResize(ew,eh);
14366         
14367     },
14368
14369     /**
14370      * Toggles the editor between standard and source edit mode.
14371      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14372      */
14373     toggleSourceEdit : function(sourceEditMode)
14374     {
14375         this.editorcore.toggleSourceEdit(sourceEditMode);
14376         
14377         if(this.editorcore.sourceEditMode){
14378             Roo.log('editor - showing textarea');
14379             
14380 //            Roo.log('in');
14381 //            Roo.log(this.syncValue());
14382             this.syncValue();
14383             this.inputEl().removeClass('hide');
14384             this.inputEl().dom.removeAttribute('tabIndex');
14385             this.inputEl().focus();
14386         }else{
14387             Roo.log('editor - hiding textarea');
14388 //            Roo.log('out')
14389 //            Roo.log(this.pushValue()); 
14390             this.pushValue();
14391             
14392             this.inputEl().addClass('hide');
14393             this.inputEl().dom.setAttribute('tabIndex', -1);
14394             //this.deferFocus();
14395         }
14396          
14397         if(this.resizable){
14398             this.setSize(this.wrap.getSize());
14399         }
14400         
14401         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14402     },
14403  
14404     // private (for BoxComponent)
14405     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14406
14407     // private (for BoxComponent)
14408     getResizeEl : function(){
14409         return this.wrap;
14410     },
14411
14412     // private (for BoxComponent)
14413     getPositionEl : function(){
14414         return this.wrap;
14415     },
14416
14417     // private
14418     initEvents : function(){
14419         this.originalValue = this.getValue();
14420     },
14421
14422 //    /**
14423 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14424 //     * @method
14425 //     */
14426 //    markInvalid : Roo.emptyFn,
14427 //    /**
14428 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14429 //     * @method
14430 //     */
14431 //    clearInvalid : Roo.emptyFn,
14432
14433     setValue : function(v){
14434         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14435         this.editorcore.pushValue();
14436     },
14437
14438      
14439     // private
14440     deferFocus : function(){
14441         this.focus.defer(10, this);
14442     },
14443
14444     // doc'ed in Field
14445     focus : function(){
14446         this.editorcore.focus();
14447         
14448     },
14449       
14450
14451     // private
14452     onDestroy : function(){
14453         
14454         
14455         
14456         if(this.rendered){
14457             
14458             for (var i =0; i < this.toolbars.length;i++) {
14459                 // fixme - ask toolbars for heights?
14460                 this.toolbars[i].onDestroy();
14461             }
14462             
14463             this.wrap.dom.innerHTML = '';
14464             this.wrap.remove();
14465         }
14466     },
14467
14468     // private
14469     onFirstFocus : function(){
14470         //Roo.log("onFirstFocus");
14471         this.editorcore.onFirstFocus();
14472          for (var i =0; i < this.toolbars.length;i++) {
14473             this.toolbars[i].onFirstFocus();
14474         }
14475         
14476     },
14477     
14478     // private
14479     syncValue : function()
14480     {   
14481         this.editorcore.syncValue();
14482     },
14483     
14484     pushValue : function()
14485     {   
14486         this.editorcore.pushValue();
14487     }
14488      
14489     
14490     // hide stuff that is not compatible
14491     /**
14492      * @event blur
14493      * @hide
14494      */
14495     /**
14496      * @event change
14497      * @hide
14498      */
14499     /**
14500      * @event focus
14501      * @hide
14502      */
14503     /**
14504      * @event specialkey
14505      * @hide
14506      */
14507     /**
14508      * @cfg {String} fieldClass @hide
14509      */
14510     /**
14511      * @cfg {String} focusClass @hide
14512      */
14513     /**
14514      * @cfg {String} autoCreate @hide
14515      */
14516     /**
14517      * @cfg {String} inputType @hide
14518      */
14519     /**
14520      * @cfg {String} invalidClass @hide
14521      */
14522     /**
14523      * @cfg {String} invalidText @hide
14524      */
14525     /**
14526      * @cfg {String} msgFx @hide
14527      */
14528     /**
14529      * @cfg {String} validateOnBlur @hide
14530      */
14531 });
14532  
14533     
14534    
14535    
14536    
14537       
14538
14539 /**
14540  * @class Roo.bootstrap.HtmlEditorToolbar1
14541  * Basic Toolbar
14542  * 
14543  * Usage:
14544  *
14545  new Roo.bootstrap.HtmlEditor({
14546     ....
14547     toolbars : [
14548         new Roo.bootstrap.HtmlEditorToolbar1({
14549             disable : { fonts: 1 , format: 1, ..., ... , ...],
14550             btns : [ .... ]
14551         })
14552     }
14553      
14554  * 
14555  * @cfg {Object} disable List of elements to disable..
14556  * @cfg {Array} btns List of additional buttons.
14557  * 
14558  * 
14559  * NEEDS Extra CSS? 
14560  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14561  */
14562  
14563 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14564 {
14565     
14566     Roo.apply(this, config);
14567     
14568     // default disabled, based on 'good practice'..
14569     this.disable = this.disable || {};
14570     Roo.applyIf(this.disable, {
14571         fontSize : true,
14572         colors : true,
14573         specialElements : true
14574     });
14575     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14576     
14577     this.editor = config.editor;
14578     this.editorcore = config.editor.editorcore;
14579     
14580     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14581     
14582     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14583     // dont call parent... till later.
14584 }
14585 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14586     
14587     
14588     bar : true,
14589     
14590     editor : false,
14591     editorcore : false,
14592     
14593     
14594     formats : [
14595         "p" ,  
14596         "h1","h2","h3","h4","h5","h6", 
14597         "pre", "code", 
14598         "abbr", "acronym", "address", "cite", "samp", "var",
14599         'div','span'
14600     ],
14601     
14602     onRender : function(ct, position)
14603     {
14604        // Roo.log("Call onRender: " + this.xtype);
14605         
14606        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14607        Roo.log(this.el);
14608        this.el.dom.style.marginBottom = '0';
14609        var _this = this;
14610        var editorcore = this.editorcore;
14611        var editor= this.editor;
14612        
14613        var children = [];
14614        var btn = function(id,cmd , toggle, handler){
14615        
14616             var  event = toggle ? 'toggle' : 'click';
14617        
14618             var a = {
14619                 size : 'sm',
14620                 xtype: 'Button',
14621                 xns: Roo.bootstrap,
14622                 glyphicon : id,
14623                 cmd : id || cmd,
14624                 enableToggle:toggle !== false,
14625                 //html : 'submit'
14626                 pressed : toggle ? false : null,
14627                 listeners : {}
14628             }
14629             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14630                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14631             }
14632             children.push(a);
14633             return a;
14634        }
14635         
14636         var style = {
14637                 xtype: 'Button',
14638                 size : 'sm',
14639                 xns: Roo.bootstrap,
14640                 glyphicon : 'font',
14641                 //html : 'submit'
14642                 menu : {
14643                     xtype: 'Menu',
14644                     xns: Roo.bootstrap,
14645                     items:  []
14646                 }
14647         };
14648         Roo.each(this.formats, function(f) {
14649             style.menu.items.push({
14650                 xtype :'MenuItem',
14651                 xns: Roo.bootstrap,
14652                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14653                 tagname : f,
14654                 listeners : {
14655                     click : function()
14656                     {
14657                         editorcore.insertTag(this.tagname);
14658                         editor.focus();
14659                     }
14660                 }
14661                 
14662             });
14663         });
14664          children.push(style);   
14665             
14666             
14667         btn('bold',false,true);
14668         btn('italic',false,true);
14669         btn('align-left', 'justifyleft',true);
14670         btn('align-center', 'justifycenter',true);
14671         btn('align-right' , 'justifyright',true);
14672         btn('link', false, false, function(btn) {
14673             //Roo.log("create link?");
14674             var url = prompt(this.createLinkText, this.defaultLinkValue);
14675             if(url && url != 'http:/'+'/'){
14676                 this.editorcore.relayCmd('createlink', url);
14677             }
14678         }),
14679         btn('list','insertunorderedlist',true);
14680         btn('pencil', false,true, function(btn){
14681                 Roo.log(this);
14682                 
14683                 this.toggleSourceEdit(btn.pressed);
14684         });
14685         /*
14686         var cog = {
14687                 xtype: 'Button',
14688                 size : 'sm',
14689                 xns: Roo.bootstrap,
14690                 glyphicon : 'cog',
14691                 //html : 'submit'
14692                 menu : {
14693                     xtype: 'Menu',
14694                     xns: Roo.bootstrap,
14695                     items:  []
14696                 }
14697         };
14698         
14699         cog.menu.items.push({
14700             xtype :'MenuItem',
14701             xns: Roo.bootstrap,
14702             html : Clean styles,
14703             tagname : f,
14704             listeners : {
14705                 click : function()
14706                 {
14707                     editorcore.insertTag(this.tagname);
14708                     editor.focus();
14709                 }
14710             }
14711             
14712         });
14713        */
14714         
14715          
14716        this.xtype = 'Navbar';
14717         
14718         for(var i=0;i< children.length;i++) {
14719             
14720             this.buttons.add(this.addxtypeChild(children[i]));
14721             
14722         }
14723         
14724         editor.on('editorevent', this.updateToolbar, this);
14725     },
14726     onBtnClick : function(id)
14727     {
14728        this.editorcore.relayCmd(id);
14729        this.editorcore.focus();
14730     },
14731     
14732     /**
14733      * Protected method that will not generally be called directly. It triggers
14734      * a toolbar update by reading the markup state of the current selection in the editor.
14735      */
14736     updateToolbar: function(){
14737
14738         if(!this.editorcore.activated){
14739             this.editor.onFirstFocus(); // is this neeed?
14740             return;
14741         }
14742
14743         var btns = this.buttons; 
14744         var doc = this.editorcore.doc;
14745         btns.get('bold').setActive(doc.queryCommandState('bold'));
14746         btns.get('italic').setActive(doc.queryCommandState('italic'));
14747         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14748         
14749         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14750         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14751         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14752         
14753         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14754         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14755          /*
14756         
14757         var ans = this.editorcore.getAllAncestors();
14758         if (this.formatCombo) {
14759             
14760             
14761             var store = this.formatCombo.store;
14762             this.formatCombo.setValue("");
14763             for (var i =0; i < ans.length;i++) {
14764                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14765                     // select it..
14766                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14767                     break;
14768                 }
14769             }
14770         }
14771         
14772         
14773         
14774         // hides menus... - so this cant be on a menu...
14775         Roo.bootstrap.MenuMgr.hideAll();
14776         */
14777         Roo.bootstrap.MenuMgr.hideAll();
14778         //this.editorsyncValue();
14779     },
14780     onFirstFocus: function() {
14781         this.buttons.each(function(item){
14782            item.enable();
14783         });
14784     },
14785     toggleSourceEdit : function(sourceEditMode){
14786         
14787           
14788         if(sourceEditMode){
14789             Roo.log("disabling buttons");
14790            this.buttons.each( function(item){
14791                 if(item.cmd != 'pencil'){
14792                     item.disable();
14793                 }
14794             });
14795           
14796         }else{
14797             Roo.log("enabling buttons");
14798             if(this.editorcore.initialized){
14799                 this.buttons.each( function(item){
14800                     item.enable();
14801                 });
14802             }
14803             
14804         }
14805         Roo.log("calling toggole on editor");
14806         // tell the editor that it's been pressed..
14807         this.editor.toggleSourceEdit(sourceEditMode);
14808        
14809     }
14810 });
14811
14812
14813
14814
14815
14816 /**
14817  * @class Roo.bootstrap.Table.AbstractSelectionModel
14818  * @extends Roo.util.Observable
14819  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14820  * implemented by descendant classes.  This class should not be directly instantiated.
14821  * @constructor
14822  */
14823 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14824     this.locked = false;
14825     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14826 };
14827
14828
14829 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14830     /** @ignore Called by the grid automatically. Do not call directly. */
14831     init : function(grid){
14832         this.grid = grid;
14833         this.initEvents();
14834     },
14835
14836     /**
14837      * Locks the selections.
14838      */
14839     lock : function(){
14840         this.locked = true;
14841     },
14842
14843     /**
14844      * Unlocks the selections.
14845      */
14846     unlock : function(){
14847         this.locked = false;
14848     },
14849
14850     /**
14851      * Returns true if the selections are locked.
14852      * @return {Boolean}
14853      */
14854     isLocked : function(){
14855         return this.locked;
14856     }
14857 });
14858 /**
14859  * @class Roo.bootstrap.Table.ColumnModel
14860  * @extends Roo.util.Observable
14861  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14862  * the columns in the table.
14863  
14864  * @constructor
14865  * @param {Object} config An Array of column config objects. See this class's
14866  * config objects for details.
14867 */
14868 Roo.bootstrap.Table.ColumnModel = function(config){
14869         /**
14870      * The config passed into the constructor
14871      */
14872     this.config = config;
14873     this.lookup = {};
14874
14875     // if no id, create one
14876     // if the column does not have a dataIndex mapping,
14877     // map it to the order it is in the config
14878     for(var i = 0, len = config.length; i < len; i++){
14879         var c = config[i];
14880         if(typeof c.dataIndex == "undefined"){
14881             c.dataIndex = i;
14882         }
14883         if(typeof c.renderer == "string"){
14884             c.renderer = Roo.util.Format[c.renderer];
14885         }
14886         if(typeof c.id == "undefined"){
14887             c.id = Roo.id();
14888         }
14889 //        if(c.editor && c.editor.xtype){
14890 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14891 //        }
14892 //        if(c.editor && c.editor.isFormField){
14893 //            c.editor = new Roo.grid.GridEditor(c.editor);
14894 //        }
14895
14896         this.lookup[c.id] = c;
14897     }
14898
14899     /**
14900      * The width of columns which have no width specified (defaults to 100)
14901      * @type Number
14902      */
14903     this.defaultWidth = 100;
14904
14905     /**
14906      * Default sortable of columns which have no sortable specified (defaults to false)
14907      * @type Boolean
14908      */
14909     this.defaultSortable = false;
14910
14911     this.addEvents({
14912         /**
14913              * @event widthchange
14914              * Fires when the width of a column changes.
14915              * @param {ColumnModel} this
14916              * @param {Number} columnIndex The column index
14917              * @param {Number} newWidth The new width
14918              */
14919             "widthchange": true,
14920         /**
14921              * @event headerchange
14922              * Fires when the text of a header changes.
14923              * @param {ColumnModel} this
14924              * @param {Number} columnIndex The column index
14925              * @param {Number} newText The new header text
14926              */
14927             "headerchange": true,
14928         /**
14929              * @event hiddenchange
14930              * Fires when a column is hidden or "unhidden".
14931              * @param {ColumnModel} this
14932              * @param {Number} columnIndex The column index
14933              * @param {Boolean} hidden true if hidden, false otherwise
14934              */
14935             "hiddenchange": true,
14936             /**
14937          * @event columnmoved
14938          * Fires when a column is moved.
14939          * @param {ColumnModel} this
14940          * @param {Number} oldIndex
14941          * @param {Number} newIndex
14942          */
14943         "columnmoved" : true,
14944         /**
14945          * @event columlockchange
14946          * Fires when a column's locked state is changed
14947          * @param {ColumnModel} this
14948          * @param {Number} colIndex
14949          * @param {Boolean} locked true if locked
14950          */
14951         "columnlockchange" : true
14952     });
14953     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14954 };
14955 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14956     /**
14957      * @cfg {String} header The header text to display in the Grid view.
14958      */
14959     /**
14960      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14961      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14962      * specified, the column's index is used as an index into the Record's data Array.
14963      */
14964     /**
14965      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14966      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14967      */
14968     /**
14969      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14970      * Defaults to the value of the {@link #defaultSortable} property.
14971      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14972      */
14973     /**
14974      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14975      */
14976     /**
14977      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14978      */
14979     /**
14980      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14981      */
14982     /**
14983      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14984      */
14985     /**
14986      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14987      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14988      * default renderer uses the raw data value.
14989      */
14990     /**
14991      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14992      */
14993
14994     /**
14995      * Returns the id of the column at the specified index.
14996      * @param {Number} index The column index
14997      * @return {String} the id
14998      */
14999     getColumnId : function(index){
15000         return this.config[index].id;
15001     },
15002
15003     /**
15004      * Returns the column for a specified id.
15005      * @param {String} id The column id
15006      * @return {Object} the column
15007      */
15008     getColumnById : function(id){
15009         return this.lookup[id];
15010     },
15011
15012     
15013     /**
15014      * Returns the column for a specified dataIndex.
15015      * @param {String} dataIndex The column dataIndex
15016      * @return {Object|Boolean} the column or false if not found
15017      */
15018     getColumnByDataIndex: function(dataIndex){
15019         var index = this.findColumnIndex(dataIndex);
15020         return index > -1 ? this.config[index] : false;
15021     },
15022     
15023     /**
15024      * Returns the index for a specified column id.
15025      * @param {String} id The column id
15026      * @return {Number} the index, or -1 if not found
15027      */
15028     getIndexById : function(id){
15029         for(var i = 0, len = this.config.length; i < len; i++){
15030             if(this.config[i].id == id){
15031                 return i;
15032             }
15033         }
15034         return -1;
15035     },
15036     
15037     /**
15038      * Returns the index for a specified column dataIndex.
15039      * @param {String} dataIndex The column dataIndex
15040      * @return {Number} the index, or -1 if not found
15041      */
15042     
15043     findColumnIndex : function(dataIndex){
15044         for(var i = 0, len = this.config.length; i < len; i++){
15045             if(this.config[i].dataIndex == dataIndex){
15046                 return i;
15047             }
15048         }
15049         return -1;
15050     },
15051     
15052     
15053     moveColumn : function(oldIndex, newIndex){
15054         var c = this.config[oldIndex];
15055         this.config.splice(oldIndex, 1);
15056         this.config.splice(newIndex, 0, c);
15057         this.dataMap = null;
15058         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15059     },
15060
15061     isLocked : function(colIndex){
15062         return this.config[colIndex].locked === true;
15063     },
15064
15065     setLocked : function(colIndex, value, suppressEvent){
15066         if(this.isLocked(colIndex) == value){
15067             return;
15068         }
15069         this.config[colIndex].locked = value;
15070         if(!suppressEvent){
15071             this.fireEvent("columnlockchange", this, colIndex, value);
15072         }
15073     },
15074
15075     getTotalLockedWidth : function(){
15076         var totalWidth = 0;
15077         for(var i = 0; i < this.config.length; i++){
15078             if(this.isLocked(i) && !this.isHidden(i)){
15079                 this.totalWidth += this.getColumnWidth(i);
15080             }
15081         }
15082         return totalWidth;
15083     },
15084
15085     getLockedCount : function(){
15086         for(var i = 0, len = this.config.length; i < len; i++){
15087             if(!this.isLocked(i)){
15088                 return i;
15089             }
15090         }
15091     },
15092
15093     /**
15094      * Returns the number of columns.
15095      * @return {Number}
15096      */
15097     getColumnCount : function(visibleOnly){
15098         if(visibleOnly === true){
15099             var c = 0;
15100             for(var i = 0, len = this.config.length; i < len; i++){
15101                 if(!this.isHidden(i)){
15102                     c++;
15103                 }
15104             }
15105             return c;
15106         }
15107         return this.config.length;
15108     },
15109
15110     /**
15111      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15112      * @param {Function} fn
15113      * @param {Object} scope (optional)
15114      * @return {Array} result
15115      */
15116     getColumnsBy : function(fn, scope){
15117         var r = [];
15118         for(var i = 0, len = this.config.length; i < len; i++){
15119             var c = this.config[i];
15120             if(fn.call(scope||this, c, i) === true){
15121                 r[r.length] = c;
15122             }
15123         }
15124         return r;
15125     },
15126
15127     /**
15128      * Returns true if the specified column is sortable.
15129      * @param {Number} col The column index
15130      * @return {Boolean}
15131      */
15132     isSortable : function(col){
15133         if(typeof this.config[col].sortable == "undefined"){
15134             return this.defaultSortable;
15135         }
15136         return this.config[col].sortable;
15137     },
15138
15139     /**
15140      * Returns the rendering (formatting) function defined for the column.
15141      * @param {Number} col The column index.
15142      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15143      */
15144     getRenderer : function(col){
15145         if(!this.config[col].renderer){
15146             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15147         }
15148         return this.config[col].renderer;
15149     },
15150
15151     /**
15152      * Sets the rendering (formatting) function for a column.
15153      * @param {Number} col The column index
15154      * @param {Function} fn The function to use to process the cell's raw data
15155      * to return HTML markup for the grid view. The render function is called with
15156      * the following parameters:<ul>
15157      * <li>Data value.</li>
15158      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15159      * <li>css A CSS style string to apply to the table cell.</li>
15160      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15161      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15162      * <li>Row index</li>
15163      * <li>Column index</li>
15164      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15165      */
15166     setRenderer : function(col, fn){
15167         this.config[col].renderer = fn;
15168     },
15169
15170     /**
15171      * Returns the width for the specified column.
15172      * @param {Number} col The column index
15173      * @return {Number}
15174      */
15175     getColumnWidth : function(col){
15176         return this.config[col].width * 1 || this.defaultWidth;
15177     },
15178
15179     /**
15180      * Sets the width for a column.
15181      * @param {Number} col The column index
15182      * @param {Number} width The new width
15183      */
15184     setColumnWidth : function(col, width, suppressEvent){
15185         this.config[col].width = width;
15186         this.totalWidth = null;
15187         if(!suppressEvent){
15188              this.fireEvent("widthchange", this, col, width);
15189         }
15190     },
15191
15192     /**
15193      * Returns the total width of all columns.
15194      * @param {Boolean} includeHidden True to include hidden column widths
15195      * @return {Number}
15196      */
15197     getTotalWidth : function(includeHidden){
15198         if(!this.totalWidth){
15199             this.totalWidth = 0;
15200             for(var i = 0, len = this.config.length; i < len; i++){
15201                 if(includeHidden || !this.isHidden(i)){
15202                     this.totalWidth += this.getColumnWidth(i);
15203                 }
15204             }
15205         }
15206         return this.totalWidth;
15207     },
15208
15209     /**
15210      * Returns the header for the specified column.
15211      * @param {Number} col The column index
15212      * @return {String}
15213      */
15214     getColumnHeader : function(col){
15215         return this.config[col].header;
15216     },
15217
15218     /**
15219      * Sets the header for a column.
15220      * @param {Number} col The column index
15221      * @param {String} header The new header
15222      */
15223     setColumnHeader : function(col, header){
15224         this.config[col].header = header;
15225         this.fireEvent("headerchange", this, col, header);
15226     },
15227
15228     /**
15229      * Returns the tooltip for the specified column.
15230      * @param {Number} col The column index
15231      * @return {String}
15232      */
15233     getColumnTooltip : function(col){
15234             return this.config[col].tooltip;
15235     },
15236     /**
15237      * Sets the tooltip for a column.
15238      * @param {Number} col The column index
15239      * @param {String} tooltip The new tooltip
15240      */
15241     setColumnTooltip : function(col, tooltip){
15242             this.config[col].tooltip = tooltip;
15243     },
15244
15245     /**
15246      * Returns the dataIndex for the specified column.
15247      * @param {Number} col The column index
15248      * @return {Number}
15249      */
15250     getDataIndex : function(col){
15251         return this.config[col].dataIndex;
15252     },
15253
15254     /**
15255      * Sets the dataIndex for a column.
15256      * @param {Number} col The column index
15257      * @param {Number} dataIndex The new dataIndex
15258      */
15259     setDataIndex : function(col, dataIndex){
15260         this.config[col].dataIndex = dataIndex;
15261     },
15262
15263     
15264     
15265     /**
15266      * Returns true if the cell is editable.
15267      * @param {Number} colIndex The column index
15268      * @param {Number} rowIndex The row index
15269      * @return {Boolean}
15270      */
15271     isCellEditable : function(colIndex, rowIndex){
15272         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15273     },
15274
15275     /**
15276      * Returns the editor defined for the cell/column.
15277      * return false or null to disable editing.
15278      * @param {Number} colIndex The column index
15279      * @param {Number} rowIndex The row index
15280      * @return {Object}
15281      */
15282     getCellEditor : function(colIndex, rowIndex){
15283         return this.config[colIndex].editor;
15284     },
15285
15286     /**
15287      * Sets if a column is editable.
15288      * @param {Number} col The column index
15289      * @param {Boolean} editable True if the column is editable
15290      */
15291     setEditable : function(col, editable){
15292         this.config[col].editable = editable;
15293     },
15294
15295
15296     /**
15297      * Returns true if the column is hidden.
15298      * @param {Number} colIndex The column index
15299      * @return {Boolean}
15300      */
15301     isHidden : function(colIndex){
15302         return this.config[colIndex].hidden;
15303     },
15304
15305
15306     /**
15307      * Returns true if the column width cannot be changed
15308      */
15309     isFixed : function(colIndex){
15310         return this.config[colIndex].fixed;
15311     },
15312
15313     /**
15314      * Returns true if the column can be resized
15315      * @return {Boolean}
15316      */
15317     isResizable : function(colIndex){
15318         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15319     },
15320     /**
15321      * Sets if a column is hidden.
15322      * @param {Number} colIndex The column index
15323      * @param {Boolean} hidden True if the column is hidden
15324      */
15325     setHidden : function(colIndex, hidden){
15326         this.config[colIndex].hidden = hidden;
15327         this.totalWidth = null;
15328         this.fireEvent("hiddenchange", this, colIndex, hidden);
15329     },
15330
15331     /**
15332      * Sets the editor for a column.
15333      * @param {Number} col The column index
15334      * @param {Object} editor The editor object
15335      */
15336     setEditor : function(col, editor){
15337         this.config[col].editor = editor;
15338     }
15339 });
15340
15341 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15342         if(typeof value == "string" && value.length < 1){
15343             return "&#160;";
15344         }
15345         return value;
15346 };
15347
15348 // Alias for backwards compatibility
15349 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15350
15351 /**
15352  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15353  * @class Roo.bootstrap.Table.RowSelectionModel
15354  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15355  * It supports multiple selections and keyboard selection/navigation. 
15356  * @constructor
15357  * @param {Object} config
15358  */
15359
15360 Roo.bootstrap.Table.RowSelectionModel = function(config){
15361     Roo.apply(this, config);
15362     this.selections = new Roo.util.MixedCollection(false, function(o){
15363         return o.id;
15364     });
15365
15366     this.last = false;
15367     this.lastActive = false;
15368
15369     this.addEvents({
15370         /**
15371              * @event selectionchange
15372              * Fires when the selection changes
15373              * @param {SelectionModel} this
15374              */
15375             "selectionchange" : true,
15376         /**
15377              * @event afterselectionchange
15378              * Fires after the selection changes (eg. by key press or clicking)
15379              * @param {SelectionModel} this
15380              */
15381             "afterselectionchange" : true,
15382         /**
15383              * @event beforerowselect
15384              * Fires when a row is selected being selected, return false to cancel.
15385              * @param {SelectionModel} this
15386              * @param {Number} rowIndex The selected index
15387              * @param {Boolean} keepExisting False if other selections will be cleared
15388              */
15389             "beforerowselect" : true,
15390         /**
15391              * @event rowselect
15392              * Fires when a row is selected.
15393              * @param {SelectionModel} this
15394              * @param {Number} rowIndex The selected index
15395              * @param {Roo.data.Record} r The record
15396              */
15397             "rowselect" : true,
15398         /**
15399              * @event rowdeselect
15400              * Fires when a row is deselected.
15401              * @param {SelectionModel} this
15402              * @param {Number} rowIndex The selected index
15403              */
15404         "rowdeselect" : true
15405     });
15406     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15407     this.locked = false;
15408 };
15409
15410 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15411     /**
15412      * @cfg {Boolean} singleSelect
15413      * True to allow selection of only one row at a time (defaults to false)
15414      */
15415     singleSelect : false,
15416
15417     // private
15418     initEvents : function(){
15419
15420         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15421             this.grid.on("mousedown", this.handleMouseDown, this);
15422         }else{ // allow click to work like normal
15423             this.grid.on("rowclick", this.handleDragableRowClick, this);
15424         }
15425
15426         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15427             "up" : function(e){
15428                 if(!e.shiftKey){
15429                     this.selectPrevious(e.shiftKey);
15430                 }else if(this.last !== false && this.lastActive !== false){
15431                     var last = this.last;
15432                     this.selectRange(this.last,  this.lastActive-1);
15433                     this.grid.getView().focusRow(this.lastActive);
15434                     if(last !== false){
15435                         this.last = last;
15436                     }
15437                 }else{
15438                     this.selectFirstRow();
15439                 }
15440                 this.fireEvent("afterselectionchange", this);
15441             },
15442             "down" : function(e){
15443                 if(!e.shiftKey){
15444                     this.selectNext(e.shiftKey);
15445                 }else if(this.last !== false && this.lastActive !== false){
15446                     var last = this.last;
15447                     this.selectRange(this.last,  this.lastActive+1);
15448                     this.grid.getView().focusRow(this.lastActive);
15449                     if(last !== false){
15450                         this.last = last;
15451                     }
15452                 }else{
15453                     this.selectFirstRow();
15454                 }
15455                 this.fireEvent("afterselectionchange", this);
15456             },
15457             scope: this
15458         });
15459
15460         var view = this.grid.view;
15461         view.on("refresh", this.onRefresh, this);
15462         view.on("rowupdated", this.onRowUpdated, this);
15463         view.on("rowremoved", this.onRemove, this);
15464     },
15465
15466     // private
15467     onRefresh : function(){
15468         var ds = this.grid.dataSource, i, v = this.grid.view;
15469         var s = this.selections;
15470         s.each(function(r){
15471             if((i = ds.indexOfId(r.id)) != -1){
15472                 v.onRowSelect(i);
15473             }else{
15474                 s.remove(r);
15475             }
15476         });
15477     },
15478
15479     // private
15480     onRemove : function(v, index, r){
15481         this.selections.remove(r);
15482     },
15483
15484     // private
15485     onRowUpdated : function(v, index, r){
15486         if(this.isSelected(r)){
15487             v.onRowSelect(index);
15488         }
15489     },
15490
15491     /**
15492      * Select records.
15493      * @param {Array} records The records to select
15494      * @param {Boolean} keepExisting (optional) True to keep existing selections
15495      */
15496     selectRecords : function(records, keepExisting){
15497         if(!keepExisting){
15498             this.clearSelections();
15499         }
15500         var ds = this.grid.dataSource;
15501         for(var i = 0, len = records.length; i < len; i++){
15502             this.selectRow(ds.indexOf(records[i]), true);
15503         }
15504     },
15505
15506     /**
15507      * Gets the number of selected rows.
15508      * @return {Number}
15509      */
15510     getCount : function(){
15511         return this.selections.length;
15512     },
15513
15514     /**
15515      * Selects the first row in the grid.
15516      */
15517     selectFirstRow : function(){
15518         this.selectRow(0);
15519     },
15520
15521     /**
15522      * Select the last row.
15523      * @param {Boolean} keepExisting (optional) True to keep existing selections
15524      */
15525     selectLastRow : function(keepExisting){
15526         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15527     },
15528
15529     /**
15530      * Selects the row immediately following the last selected row.
15531      * @param {Boolean} keepExisting (optional) True to keep existing selections
15532      */
15533     selectNext : function(keepExisting){
15534         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15535             this.selectRow(this.last+1, keepExisting);
15536             this.grid.getView().focusRow(this.last);
15537         }
15538     },
15539
15540     /**
15541      * Selects the row that precedes the last selected row.
15542      * @param {Boolean} keepExisting (optional) True to keep existing selections
15543      */
15544     selectPrevious : function(keepExisting){
15545         if(this.last){
15546             this.selectRow(this.last-1, keepExisting);
15547             this.grid.getView().focusRow(this.last);
15548         }
15549     },
15550
15551     /**
15552      * Returns the selected records
15553      * @return {Array} Array of selected records
15554      */
15555     getSelections : function(){
15556         return [].concat(this.selections.items);
15557     },
15558
15559     /**
15560      * Returns the first selected record.
15561      * @return {Record}
15562      */
15563     getSelected : function(){
15564         return this.selections.itemAt(0);
15565     },
15566
15567
15568     /**
15569      * Clears all selections.
15570      */
15571     clearSelections : function(fast){
15572         if(this.locked) return;
15573         if(fast !== true){
15574             var ds = this.grid.dataSource;
15575             var s = this.selections;
15576             s.each(function(r){
15577                 this.deselectRow(ds.indexOfId(r.id));
15578             }, this);
15579             s.clear();
15580         }else{
15581             this.selections.clear();
15582         }
15583         this.last = false;
15584     },
15585
15586
15587     /**
15588      * Selects all rows.
15589      */
15590     selectAll : function(){
15591         if(this.locked) return;
15592         this.selections.clear();
15593         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15594             this.selectRow(i, true);
15595         }
15596     },
15597
15598     /**
15599      * Returns True if there is a selection.
15600      * @return {Boolean}
15601      */
15602     hasSelection : function(){
15603         return this.selections.length > 0;
15604     },
15605
15606     /**
15607      * Returns True if the specified row is selected.
15608      * @param {Number/Record} record The record or index of the record to check
15609      * @return {Boolean}
15610      */
15611     isSelected : function(index){
15612         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15613         return (r && this.selections.key(r.id) ? true : false);
15614     },
15615
15616     /**
15617      * Returns True if the specified record id is selected.
15618      * @param {String} id The id of record to check
15619      * @return {Boolean}
15620      */
15621     isIdSelected : function(id){
15622         return (this.selections.key(id) ? true : false);
15623     },
15624
15625     // private
15626     handleMouseDown : function(e, t){
15627         var view = this.grid.getView(), rowIndex;
15628         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15629             return;
15630         };
15631         if(e.shiftKey && this.last !== false){
15632             var last = this.last;
15633             this.selectRange(last, rowIndex, e.ctrlKey);
15634             this.last = last; // reset the last
15635             view.focusRow(rowIndex);
15636         }else{
15637             var isSelected = this.isSelected(rowIndex);
15638             if(e.button !== 0 && isSelected){
15639                 view.focusRow(rowIndex);
15640             }else if(e.ctrlKey && isSelected){
15641                 this.deselectRow(rowIndex);
15642             }else if(!isSelected){
15643                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15644                 view.focusRow(rowIndex);
15645             }
15646         }
15647         this.fireEvent("afterselectionchange", this);
15648     },
15649     // private
15650     handleDragableRowClick :  function(grid, rowIndex, e) 
15651     {
15652         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15653             this.selectRow(rowIndex, false);
15654             grid.view.focusRow(rowIndex);
15655              this.fireEvent("afterselectionchange", this);
15656         }
15657     },
15658     
15659     /**
15660      * Selects multiple rows.
15661      * @param {Array} rows Array of the indexes of the row to select
15662      * @param {Boolean} keepExisting (optional) True to keep existing selections
15663      */
15664     selectRows : function(rows, keepExisting){
15665         if(!keepExisting){
15666             this.clearSelections();
15667         }
15668         for(var i = 0, len = rows.length; i < len; i++){
15669             this.selectRow(rows[i], true);
15670         }
15671     },
15672
15673     /**
15674      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15675      * @param {Number} startRow The index of the first row in the range
15676      * @param {Number} endRow The index of the last row in the range
15677      * @param {Boolean} keepExisting (optional) True to retain existing selections
15678      */
15679     selectRange : function(startRow, endRow, keepExisting){
15680         if(this.locked) return;
15681         if(!keepExisting){
15682             this.clearSelections();
15683         }
15684         if(startRow <= endRow){
15685             for(var i = startRow; i <= endRow; i++){
15686                 this.selectRow(i, true);
15687             }
15688         }else{
15689             for(var i = startRow; i >= endRow; i--){
15690                 this.selectRow(i, true);
15691             }
15692         }
15693     },
15694
15695     /**
15696      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15697      * @param {Number} startRow The index of the first row in the range
15698      * @param {Number} endRow The index of the last row in the range
15699      */
15700     deselectRange : function(startRow, endRow, preventViewNotify){
15701         if(this.locked) return;
15702         for(var i = startRow; i <= endRow; i++){
15703             this.deselectRow(i, preventViewNotify);
15704         }
15705     },
15706
15707     /**
15708      * Selects a row.
15709      * @param {Number} row The index of the row to select
15710      * @param {Boolean} keepExisting (optional) True to keep existing selections
15711      */
15712     selectRow : function(index, keepExisting, preventViewNotify){
15713         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15714         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15715             if(!keepExisting || this.singleSelect){
15716                 this.clearSelections();
15717             }
15718             var r = this.grid.dataSource.getAt(index);
15719             this.selections.add(r);
15720             this.last = this.lastActive = index;
15721             if(!preventViewNotify){
15722                 this.grid.getView().onRowSelect(index);
15723             }
15724             this.fireEvent("rowselect", this, index, r);
15725             this.fireEvent("selectionchange", this);
15726         }
15727     },
15728
15729     /**
15730      * Deselects a row.
15731      * @param {Number} row The index of the row to deselect
15732      */
15733     deselectRow : function(index, preventViewNotify){
15734         if(this.locked) return;
15735         if(this.last == index){
15736             this.last = false;
15737         }
15738         if(this.lastActive == index){
15739             this.lastActive = false;
15740         }
15741         var r = this.grid.dataSource.getAt(index);
15742         this.selections.remove(r);
15743         if(!preventViewNotify){
15744             this.grid.getView().onRowDeselect(index);
15745         }
15746         this.fireEvent("rowdeselect", this, index);
15747         this.fireEvent("selectionchange", this);
15748     },
15749
15750     // private
15751     restoreLast : function(){
15752         if(this._last){
15753             this.last = this._last;
15754         }
15755     },
15756
15757     // private
15758     acceptsNav : function(row, col, cm){
15759         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15760     },
15761
15762     // private
15763     onEditorKey : function(field, e){
15764         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15765         if(k == e.TAB){
15766             e.stopEvent();
15767             ed.completeEdit();
15768             if(e.shiftKey){
15769                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15770             }else{
15771                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15772             }
15773         }else if(k == e.ENTER && !e.ctrlKey){
15774             e.stopEvent();
15775             ed.completeEdit();
15776             if(e.shiftKey){
15777                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15778             }else{
15779                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15780             }
15781         }else if(k == e.ESC){
15782             ed.cancelEdit();
15783         }
15784         if(newCell){
15785             g.startEditing(newCell[0], newCell[1]);
15786         }
15787     }
15788 });/*
15789  * - LGPL
15790  *
15791  * element
15792  * 
15793  */
15794
15795 /**
15796  * @class Roo.bootstrap.MessageBar
15797  * @extends Roo.bootstrap.Component
15798  * Bootstrap MessageBar class
15799  * @cfg {String} html contents of the MessageBar
15800  * @cfg {String} weight (info | success | warning | danger) default info
15801  * @cfg {String} beforeClass insert the bar before the given class
15802  * @cfg {Boolean} closable (true | false) default false
15803  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15804  * 
15805  * @constructor
15806  * Create a new Element
15807  * @param {Object} config The config object
15808  */
15809
15810 Roo.bootstrap.MessageBar = function(config){
15811     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15812 };
15813
15814 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15815     
15816     html: '',
15817     weight: 'info',
15818     closable: false,
15819     fixed: false,
15820     beforeClass: 'bootstrap-sticky-wrap',
15821     
15822     getAutoCreate : function(){
15823         
15824         var cfg = {
15825             tag: 'div',
15826             cls: 'alert alert-dismissable alert-' + this.weight,
15827             cn: [
15828                 {
15829                     tag: 'span',
15830                     cls: 'message',
15831                     html: this.html || ''
15832                 }
15833             ]
15834         }
15835         
15836         if(this.fixed){
15837             cfg.cls += ' alert-messages-fixed';
15838         }
15839         
15840         if(this.closable){
15841             cfg.cn.push({
15842                 tag: 'button',
15843                 cls: 'close',
15844                 html: 'x'
15845             });
15846         }
15847         
15848         return cfg;
15849     },
15850     
15851     onRender : function(ct, position)
15852     {
15853         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15854         
15855         if(!this.el){
15856             var cfg = Roo.apply({},  this.getAutoCreate());
15857             cfg.id = Roo.id();
15858             
15859             if (this.cls) {
15860                 cfg.cls += ' ' + this.cls;
15861             }
15862             if (this.style) {
15863                 cfg.style = this.style;
15864             }
15865             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15866             
15867             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15868         }
15869         
15870         this.el.select('>button.close').on('click', this.hide, this);
15871         
15872     },
15873     
15874     show : function()
15875     {
15876         if (!this.rendered) {
15877             this.render();
15878         }
15879         
15880         this.el.show();
15881         
15882         this.fireEvent('show', this);
15883         
15884     },
15885     
15886     hide : function()
15887     {
15888         if (!this.rendered) {
15889             this.render();
15890         }
15891         
15892         this.el.hide();
15893         
15894         this.fireEvent('hide', this);
15895     },
15896     
15897     update : function()
15898     {
15899 //        var e = this.el.dom.firstChild;
15900 //        
15901 //        if(this.closable){
15902 //            e = e.nextSibling;
15903 //        }
15904 //        
15905 //        e.data = this.html || '';
15906
15907         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15908     }
15909    
15910 });
15911
15912  
15913
15914