Roo/bootstrap/Table.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position){
306         
307         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472     
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = this.html || cfg.html;
489         
490         if (this.toggle == true) {
491             cfg={
492                 tag: 'div',
493                 cls: 'slider-frame roo-button',
494                 cn: [
495                     {
496                         tag: 'span',
497                         'data-on-text':'ON',
498                         'data-off-text':'OFF',
499                         cls: 'slider-button',
500                         html: this.offtext
501                     }
502                 ]
503             };
504             
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 cfg.cls += ' '+this.weight;
507             }
508             
509             return cfg;
510         }
511         
512         if (this.isClose) {
513             cfg.cls += ' close';
514             
515             cfg["aria-hidden"] = true;
516             
517             cfg.html = "&times;";
518             
519             return cfg;
520         }
521         
522          
523         if (this.theme==='default') {
524             cfg.cls = 'btn roo-button';
525             
526             //if (this.parentType != 'Navbar') {
527             this.weight = this.weight.length ?  this.weight : 'default';
528             //}
529             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
530                 
531                 cfg.cls += ' btn-' + this.weight;
532             }
533         } else if (this.theme==='glow') {
534             
535             cfg.tag = 'a';
536             cfg.cls = 'btn-glow roo-button';
537             
538             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
539                 
540                 cfg.cls += ' ' + this.weight;
541             }
542         }
543    
544         
545         if (this.inverse) {
546             this.cls += ' inverse';
547         }
548         
549         
550         if (this.active) {
551             cfg.cls += ' active';
552         }
553         
554         if (this.disabled) {
555             cfg.disabled = 'disabled';
556         }
557         
558         if (this.items) {
559             Roo.log('changing to ul' );
560             cfg.tag = 'ul';
561             this.glyphicon = 'caret';
562         }
563         
564         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
565          
566         //gsRoo.log(this.parentType);
567         if (this.parentType === 'Navbar' && !this.parent().bar) {
568             Roo.log('changing to li?');
569             
570             cfg.tag = 'li';
571             
572             cfg.cls = '';
573             cfg.cn =  [{
574                 tag : 'a',
575                 cls : 'roo-button',
576                 html : this.html,
577                 href : this.href || '#'
578             }];
579             if (this.menu) {
580                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
581                 cfg.cls += ' dropdown';
582             }   
583             
584             delete cfg.html;
585             
586         }
587         
588        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
589         
590         if (this.glyphicon) {
591             cfg.html = ' ' + cfg.html;
592             
593             cfg.cn = [
594                 {
595                     tag: 'span',
596                     cls: 'glyphicon glyphicon-' + this.glyphicon
597                 }
598             ];
599         }
600         
601         if (this.badge) {
602             cfg.html += ' ';
603             
604             cfg.tag = 'a';
605             
606 //            cfg.cls='btn roo-button';
607             
608             cfg.href=this.href;
609             
610             var value = cfg.html;
611             
612             if(this.glyphicon){
613                 value = {
614                             tag: 'span',
615                             cls: 'glyphicon glyphicon-' + this.glyphicon,
616                             html: this.html
617                         };
618                 
619             }
620             
621             cfg.cn = [
622                 value,
623                 {
624                     tag: 'span',
625                     cls: 'badge',
626                     html: this.badge
627                 }
628             ];
629             
630             cfg.html='';
631         }
632         
633         if (this.menu) {
634             cfg.cls += ' dropdown';
635             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
636         }
637         
638         if (cfg.tag !== 'a' && this.href !== '') {
639             throw "Tag must be a to set href.";
640         } else if (this.href.length > 0) {
641             cfg.href = this.href;
642         }
643         
644         if(this.removeClass){
645             cfg.cls = '';
646         }
647         
648         if(this.target){
649             cfg.target = this.target;
650         }
651         
652         return cfg;
653     },
654     initEvents: function() {
655        // Roo.log('init events?');
656 //        Roo.log(this.el.dom);
657        if (this.el.hasClass('roo-button')) {
658             this.el.on('click', this.onClick, this);
659        } else {
660             this.el.select('.roo-button').on('click', this.onClick, this);
661        }
662        
663        
664         
665     },
666     onClick : function(e)
667     {
668         if (this.disabled) {
669             return;
670         }
671         
672         Roo.log('button on click ');
673         if(this.preventDefault){
674             e.preventDefault();
675         }
676         if (this.pressed === true || this.pressed === false) {
677             this.pressed = !this.pressed;
678             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679             this.fireEvent('toggle', this, e, this.pressed);
680         }
681         
682         
683         this.fireEvent('click', this, e);
684     },
685     
686     /**
687      * Enables this button
688      */
689     enable : function()
690     {
691         this.disabled = false;
692         this.el.removeClass('disabled');
693     },
694     
695     /**
696      * Disable this button
697      */
698     disable : function()
699     {
700         this.disabled = true;
701         this.el.addClass('disabled');
702     },
703      /**
704      * sets the active state on/off, 
705      * @param {Boolean} state (optional) Force a particular state
706      */
707     setActive : function(v) {
708         
709         this.el[v ? 'addClass' : 'removeClass']('active');
710     },
711      /**
712      * toggles the current active state 
713      */
714     toggleActive : function()
715     {
716        var active = this.el.hasClass('active');
717        this.setActive(!active);
718        
719         
720     }
721     
722     
723     
724 });
725
726  /*
727  * - LGPL
728  *
729  * column
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.Column
735  * @extends Roo.bootstrap.Component
736  * Bootstrap Column class
737  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739  * @cfg {Number} md colspan out of 12 for computer-sized screens
740  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741  * @cfg {String} html content of column.
742  * 
743  * @constructor
744  * Create a new Column
745  * @param {Object} config The config object
746  */
747
748 Roo.bootstrap.Column = function(config){
749     Roo.bootstrap.Column.superclass.constructor.call(this, config);
750 };
751
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
753     
754     xs: null,
755     sm: null,
756     md: null,
757     lg: null,
758     html: '',
759     offset: 0,
760     
761     getAutoCreate : function(){
762         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
763         
764         cfg = {
765             tag: 'div',
766             cls: 'column'
767         };
768         
769         var settings=this;
770         ['xs','sm','md','lg'].map(function(size){
771             if (settings[size]) {
772                 cfg.cls += ' col-' + size + '-' + settings[size];
773             }
774         });
775         if (this.html.length) {
776             cfg.html = this.html;
777         }
778         
779         return cfg;
780     }
781    
782 });
783
784  
785
786  /*
787  * - LGPL
788  *
789  * page container.
790  * 
791  */
792
793
794 /**
795  * @class Roo.bootstrap.Container
796  * @extends Roo.bootstrap.Component
797  * Bootstrap Container class
798  * @cfg {Boolean} jumbotron is it a jumbotron element
799  * @cfg {String} html content of element
800  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802  * @cfg {String} header content of header (for panel)
803  * @cfg {String} footer content of footer (for panel)
804  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
805  *     
806  * @constructor
807  * Create a new Container
808  * @param {Object} config The config object
809  */
810
811 Roo.bootstrap.Container = function(config){
812     Roo.bootstrap.Container.superclass.constructor.call(this, config);
813 };
814
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
816     
817     jumbotron : false,
818     well: '',
819     panel : '',
820     header: '',
821     footer : '',
822     sticky: '',
823   
824      
825     getChildContainer : function() {
826         
827         if(!this.el){
828             return false;
829         }
830         
831         if (this.panel.length) {
832             return this.el.select('.panel-body',true).first();
833         }
834         
835         return this.el;
836     },
837     
838     
839     getAutoCreate : function(){
840         
841         var cfg = {
842             html : '',
843             cls : ''
844         };
845         if (this.jumbotron) {
846             cfg.cls = 'jumbotron';
847         }
848         if (this.cls) {
849             cfg.cls = this.cls + '';
850         }
851         
852         if (this.sticky.length) {
853             
854             var bd = Roo.get(document.body);
855             if (!bd.hasClass('bootstrap-sticky')) {
856                 bd.addClass('bootstrap-sticky');
857                 Roo.select('html',true).setStyle('height', '100%');
858             }
859              
860             cfg.cls += 'bootstrap-sticky-' + this.sticky;
861         }
862         
863         
864         if (this.well.length) {
865             switch (this.well) {
866                 case 'lg':
867                 case 'sm':
868                     cfg.cls +=' well well-' +this.well;
869                     break;
870                 default:
871                     cfg.cls +=' well';
872                     break;
873             }
874         }
875         
876         var body = cfg;
877         
878         if (this.panel.length) {
879             cfg.cls += ' panel panel-' + this.panel;
880             cfg.cn = [];
881             if (this.header.length) {
882                 cfg.cn.push({
883                     
884                     cls : 'panel-heading',
885                     cn : [{
886                         tag: 'h3',
887                         cls : 'panel-title',
888                         html : this.header
889                     }]
890                     
891                 });
892             }
893             body = false;
894             cfg.cn.push({
895                 cls : 'panel-body',
896                 html : this.html
897             });
898             
899             
900             if (this.footer.length) {
901                 cfg.cn.push({
902                     cls : 'panel-footer',
903                     html : this.footer
904                     
905                 });
906             }
907             
908         }
909         if (body) {
910             body.html = this.html || cfg.html;
911         }
912         if (!cfg.cls.length) {
913             cfg.cls =  'container';
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  /*
922  * - LGPL
923  *
924  * image
925  * 
926  */
927
928
929 /**
930  * @class Roo.bootstrap.Img
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Img class
933  * @cfg {Boolean} imgResponsive false | true
934  * @cfg {String} border rounded | circle | thumbnail
935  * @cfg {String} src image source
936  * @cfg {String} alt image alternative text
937  * @cfg {String} href a tag href
938  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
939  * 
940  * @constructor
941  * Create a new Input
942  * @param {Object} config The config object
943  */
944
945 Roo.bootstrap.Img = function(config){
946     Roo.bootstrap.Img.superclass.constructor.call(this, config);
947     
948     this.addEvents({
949         // img events
950         /**
951          * @event click
952          * The img click event for the img.
953          * @param {Roo.EventObject} e
954          */
955         "click" : true
956     });
957 };
958
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
960     
961     imgResponsive: true,
962     border: '',
963     src: '',
964     href: false,
965     target: false,
966
967     getAutoCreate : function(){
968         
969         var cfg = {
970             tag: 'img',
971             cls: 'img-responsive',
972             html : null
973         }
974         
975         cfg.html = this.html || cfg.html;
976         
977         cfg.src = this.src || cfg.src;
978         
979         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980             cfg.cls += ' img-' + this.border;
981         }
982         
983         if(this.alt){
984             cfg.alt = this.alt;
985         }
986         
987         if(this.href){
988             var a = {
989                 tag: 'a',
990                 href: this.href,
991                 cn: [
992                     cfg
993                 ]
994             }
995             
996             if(this.target){
997                 a.target = this.target;
998             }
999             
1000         }
1001         
1002         
1003         return (this.href) ? a : cfg;
1004     },
1005     
1006     initEvents: function() {
1007         
1008         if(!this.href){
1009             this.el.on('click', this.onClick, this);
1010         }
1011     },
1012     
1013     onClick : function(e)
1014     {
1015         Roo.log('img onclick');
1016         this.fireEvent('click', this, e);
1017     }
1018    
1019 });
1020
1021  /*
1022  * - LGPL
1023  *
1024  * header
1025  * 
1026  */
1027
1028 /**
1029  * @class Roo.bootstrap.Header
1030  * @extends Roo.bootstrap.Component
1031  * Bootstrap Header class
1032  * @cfg {String} html content of header
1033  * @cfg {Number} level (1|2|3|4|5|6) default 1
1034  * 
1035  * @constructor
1036  * Create a new Header
1037  * @param {Object} config The config object
1038  */
1039
1040
1041 Roo.bootstrap.Header  = function(config){
1042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1046     
1047     //href : false,
1048     html : false,
1049     level : 1,
1050     
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag: 'h' + (1 *this.level),
1057             html: this.html || 'fill in html'
1058         } ;
1059         
1060         return cfg;
1061     }
1062    
1063 });
1064
1065  
1066
1067  /*
1068  * Based on:
1069  * Ext JS Library 1.1.1
1070  * Copyright(c) 2006-2007, Ext JS, LLC.
1071  *
1072  * Originally Released Under LGPL - original licence link has changed is not relivant.
1073  *
1074  * Fork - LGPL
1075  * <script type="text/javascript">
1076  */
1077  
1078 /**
1079  * @class Roo.bootstrap.MenuMgr
1080  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1081  * @singleton
1082  */
1083 Roo.bootstrap.MenuMgr = function(){
1084    var menus, active, groups = {}, attached = false, lastShow = new Date();
1085
1086    // private - called when first menu is created
1087    function init(){
1088        menus = {};
1089        active = new Roo.util.MixedCollection();
1090        Roo.get(document).addKeyListener(27, function(){
1091            if(active.length > 0){
1092                hideAll();
1093            }
1094        });
1095    }
1096
1097    // private
1098    function hideAll(){
1099        if(active && active.length > 0){
1100            var c = active.clone();
1101            c.each(function(m){
1102                m.hide();
1103            });
1104        }
1105    }
1106
1107    // private
1108    function onHide(m){
1109        active.remove(m);
1110        if(active.length < 1){
1111            Roo.get(document).un("mouseup", onMouseDown);
1112             
1113            attached = false;
1114        }
1115    }
1116
1117    // private
1118    function onShow(m){
1119        var last = active.last();
1120        lastShow = new Date();
1121        active.add(m);
1122        if(!attached){
1123           Roo.get(document).on("mouseup", onMouseDown);
1124            
1125            attached = true;
1126        }
1127        if(m.parentMenu){
1128           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129           m.parentMenu.activeChild = m;
1130        }else if(last && last.isVisible()){
1131           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1132        }
1133    }
1134
1135    // private
1136    function onBeforeHide(m){
1137        if(m.activeChild){
1138            m.activeChild.hide();
1139        }
1140        if(m.autoHideTimer){
1141            clearTimeout(m.autoHideTimer);
1142            delete m.autoHideTimer;
1143        }
1144    }
1145
1146    // private
1147    function onBeforeShow(m){
1148        var pm = m.parentMenu;
1149        if(!pm && !m.allowOtherMenus){
1150            hideAll();
1151        }else if(pm && pm.activeChild && active != m){
1152            pm.activeChild.hide();
1153        }
1154    }
1155
1156    // private
1157    function onMouseDown(e){
1158         Roo.log("on MouseDown");
1159         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1160            hideAll();
1161         }
1162         
1163         
1164    }
1165
1166    // private
1167    function onBeforeCheck(mi, state){
1168        if(state){
1169            var g = groups[mi.group];
1170            for(var i = 0, l = g.length; i < l; i++){
1171                if(g[i] != mi){
1172                    g[i].setChecked(false);
1173                }
1174            }
1175        }
1176    }
1177
1178    return {
1179
1180        /**
1181         * Hides all menus that are currently visible
1182         */
1183        hideAll : function(){
1184             hideAll();  
1185        },
1186
1187        // private
1188        register : function(menu){
1189            if(!menus){
1190                init();
1191            }
1192            menus[menu.id] = menu;
1193            menu.on("beforehide", onBeforeHide);
1194            menu.on("hide", onHide);
1195            menu.on("beforeshow", onBeforeShow);
1196            menu.on("show", onShow);
1197            var g = menu.group;
1198            if(g && menu.events["checkchange"]){
1199                if(!groups[g]){
1200                    groups[g] = [];
1201                }
1202                groups[g].push(menu);
1203                menu.on("checkchange", onCheck);
1204            }
1205        },
1206
1207         /**
1208          * Returns a {@link Roo.menu.Menu} object
1209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210          * be used to generate and return a new Menu instance.
1211          */
1212        get : function(menu){
1213            if(typeof menu == "string"){ // menu id
1214                return menus[menu];
1215            }else if(menu.events){  // menu instance
1216                return menu;
1217            }
1218            /*else if(typeof menu.length == 'number'){ // array of menu items?
1219                return new Roo.bootstrap.Menu({items:menu});
1220            }else{ // otherwise, must be a config
1221                return new Roo.bootstrap.Menu(menu);
1222            }
1223            */
1224            return false;
1225        },
1226
1227        // private
1228        unregister : function(menu){
1229            delete menus[menu.id];
1230            menu.un("beforehide", onBeforeHide);
1231            menu.un("hide", onHide);
1232            menu.un("beforeshow", onBeforeShow);
1233            menu.un("show", onShow);
1234            var g = menu.group;
1235            if(g && menu.events["checkchange"]){
1236                groups[g].remove(menu);
1237                menu.un("checkchange", onCheck);
1238            }
1239        },
1240
1241        // private
1242        registerCheckable : function(menuItem){
1243            var g = menuItem.group;
1244            if(g){
1245                if(!groups[g]){
1246                    groups[g] = [];
1247                }
1248                groups[g].push(menuItem);
1249                menuItem.on("beforecheckchange", onBeforeCheck);
1250            }
1251        },
1252
1253        // private
1254        unregisterCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                groups[g].remove(menuItem);
1258                menuItem.un("beforecheckchange", onBeforeCheck);
1259            }
1260        }
1261    };
1262 }();/*
1263  * - LGPL
1264  *
1265  * menu
1266  * 
1267  */
1268
1269 /**
1270  * @class Roo.bootstrap.Menu
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Menu class - container for MenuItems
1273  * @cfg {String} type type of menu
1274  * 
1275  * @constructor
1276  * Create a new Menu
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Menu = function(config){
1282     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283     if (this.registerMenu) {
1284         Roo.bootstrap.MenuMgr.register(this);
1285     }
1286     this.addEvents({
1287         /**
1288          * @event beforeshow
1289          * Fires before this menu is displayed
1290          * @param {Roo.menu.Menu} this
1291          */
1292         beforeshow : true,
1293         /**
1294          * @event beforehide
1295          * Fires before this menu is hidden
1296          * @param {Roo.menu.Menu} this
1297          */
1298         beforehide : true,
1299         /**
1300          * @event show
1301          * Fires after this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         show : true,
1305         /**
1306          * @event hide
1307          * Fires after this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         hide : true,
1311         /**
1312          * @event click
1313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314          * @param {Roo.menu.Menu} this
1315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316          * @param {Roo.EventObject} e
1317          */
1318         click : true,
1319         /**
1320          * @event mouseover
1321          * Fires when the mouse is hovering over this menu
1322          * @param {Roo.menu.Menu} this
1323          * @param {Roo.EventObject} e
1324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1325          */
1326         mouseover : true,
1327         /**
1328          * @event mouseout
1329          * Fires when the mouse exits this menu
1330          * @param {Roo.menu.Menu} this
1331          * @param {Roo.EventObject} e
1332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1333          */
1334         mouseout : true,
1335         /**
1336          * @event itemclick
1337          * Fires when a menu item contained in this menu is clicked
1338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339          * @param {Roo.EventObject} e
1340          */
1341         itemclick: true
1342     });
1343     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1347     
1348    /// html : false,
1349     //align : '',
1350     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1351     type: false,
1352     /**
1353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1354      */
1355     registerMenu : true,
1356     
1357     menuItems :false, // stores the menu items..
1358     
1359     hidden:true,
1360     
1361     parentMenu : false,
1362     
1363     getChildContainer : function() {
1364         return this.el;  
1365     },
1366     
1367     getAutoCreate : function(){
1368          
1369         //if (['right'].indexOf(this.align)!==-1) {
1370         //    cfg.cn[1].cls += ' pull-right'
1371         //}
1372         var cfg = {
1373             tag : 'ul',
1374             cls : 'dropdown-menu' ,
1375             style : 'z-index:1000'
1376             
1377         }
1378         
1379         if (this.type === 'submenu') {
1380             cfg.cls = 'submenu active'
1381         }
1382         
1383         return cfg;
1384     },
1385     initEvents : function() {
1386         
1387        // Roo.log("ADD event");
1388        // Roo.log(this.triggerEl.dom);
1389         this.triggerEl.on('click', this.onTriggerPress, this);
1390         this.triggerEl.addClass('dropdown-toggle');
1391         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1392
1393         this.el.on("mouseover", this.onMouseOver, this);
1394         this.el.on("mouseout", this.onMouseOut, this);
1395         
1396         
1397     },
1398     findTargetItem : function(e){
1399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1400         if(!t){
1401             return false;
1402         }
1403         //Roo.log(t);         Roo.log(t.id);
1404         if(t && t.id){
1405             //Roo.log(this.menuitems);
1406             return this.menuitems.get(t.id);
1407             
1408             //return this.items.get(t.menuItemId);
1409         }
1410         
1411         return false;
1412     },
1413     onClick : function(e){
1414         Roo.log("menu.onClick");
1415         var t = this.findTargetItem(e);
1416         if(!t){
1417             return;
1418         }
1419         Roo.log(e);
1420         /*
1421         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1422             if(t == this.activeItem && t.shouldDeactivate(e)){
1423                 this.activeItem.deactivate();
1424                 delete this.activeItem;
1425                 return;
1426             }
1427             if(t.canActivate){
1428                 this.setActiveItem(t, true);
1429             }
1430             return;
1431             
1432             
1433         }
1434         */
1435         Roo.log('pass click event');
1436         
1437         t.onClick(e);
1438         
1439         this.fireEvent("click", this, t, e);
1440         
1441         this.hide();
1442     },
1443      onMouseOver : function(e){
1444         var t  = this.findTargetItem(e);
1445         //Roo.log(t);
1446         //if(t){
1447         //    if(t.canActivate && !t.disabled){
1448         //        this.setActiveItem(t, true);
1449         //    }
1450         //}
1451         
1452         this.fireEvent("mouseover", this, e, t);
1453     },
1454     isVisible : function(){
1455         return !this.hidden;
1456     },
1457      onMouseOut : function(e){
1458         var t  = this.findTargetItem(e);
1459         
1460         //if(t ){
1461         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1462         //        this.activeItem.deactivate();
1463         //        delete this.activeItem;
1464         //    }
1465         //}
1466         this.fireEvent("mouseout", this, e, t);
1467     },
1468     
1469     
1470     /**
1471      * Displays this menu relative to another element
1472      * @param {String/HTMLElement/Roo.Element} element The element to align to
1473      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474      * the element (defaults to this.defaultAlign)
1475      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1476      */
1477     show : function(el, pos, parentMenu){
1478         this.parentMenu = parentMenu;
1479         if(!this.el){
1480             this.render();
1481         }
1482         this.fireEvent("beforeshow", this);
1483         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1484     },
1485      /**
1486      * Displays this menu at a specific xy position
1487      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489      */
1490     showAt : function(xy, parentMenu, /* private: */_e){
1491         this.parentMenu = parentMenu;
1492         if(!this.el){
1493             this.render();
1494         }
1495         if(_e !== false){
1496             this.fireEvent("beforeshow", this);
1497             
1498             //xy = this.el.adjustForConstraints(xy);
1499         }
1500         //this.el.setXY(xy);
1501         //this.el.show();
1502         this.hideMenuItems();
1503         this.hidden = false;
1504         this.triggerEl.addClass('open');
1505         this.focus();
1506         this.fireEvent("show", this);
1507     },
1508     
1509     focus : function(){
1510         return;
1511         if(!this.hidden){
1512             this.doFocus.defer(50, this);
1513         }
1514     },
1515
1516     doFocus : function(){
1517         if(!this.hidden){
1518             this.focusEl.focus();
1519         }
1520     },
1521
1522     /**
1523      * Hides this menu and optionally all parent menus
1524      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1525      */
1526     hide : function(deep){
1527         
1528         this.hideMenuItems();
1529         if(this.el && this.isVisible()){
1530             this.fireEvent("beforehide", this);
1531             if(this.activeItem){
1532                 this.activeItem.deactivate();
1533                 this.activeItem = null;
1534             }
1535             this.triggerEl.removeClass('open');;
1536             this.hidden = true;
1537             this.fireEvent("hide", this);
1538         }
1539         if(deep === true && this.parentMenu){
1540             this.parentMenu.hide(true);
1541         }
1542     },
1543     
1544     onTriggerPress  : function(e)
1545     {
1546         
1547         Roo.log('trigger press');
1548         //Roo.log(e.getTarget());
1549        // Roo.log(this.triggerEl.dom);
1550         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1551             return;
1552         }
1553         if (this.isVisible()) {
1554             Roo.log('hide');
1555             this.hide();
1556         } else {
1557             this.show(this.triggerEl, false, false);
1558         }
1559         
1560         
1561     },
1562     
1563          
1564        
1565     
1566     hideMenuItems : function()
1567     {
1568         //$(backdrop).remove()
1569         Roo.select('.open',true).each(function(aa) {
1570             
1571             aa.removeClass('open');
1572           //var parent = getParent($(this))
1573           //var relatedTarget = { relatedTarget: this }
1574           
1575            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576           //if (e.isDefaultPrevented()) return
1577            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1578         })
1579     },
1580     addxtypeChild : function (tree, cntr) {
1581         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1582           
1583         this.menuitems.add(comp);
1584         return comp;
1585
1586     },
1587     getEl : function()
1588     {
1589         Roo.log(this.el);
1590         return this.el;
1591     }
1592 });
1593
1594  
1595  /*
1596  * - LGPL
1597  *
1598  * menu item
1599  * 
1600  */
1601
1602
1603 /**
1604  * @class Roo.bootstrap.MenuItem
1605  * @extends Roo.bootstrap.Component
1606  * Bootstrap MenuItem class
1607  * @cfg {String} html the menu label
1608  * @cfg {String} href the link
1609  * @cfg {Boolean} preventDefault (true | false) default true
1610  * 
1611  * 
1612  * @constructor
1613  * Create a new MenuItem
1614  * @param {Object} config The config object
1615  */
1616
1617
1618 Roo.bootstrap.MenuItem = function(config){
1619     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1620     this.addEvents({
1621         // raw events
1622         /**
1623          * @event click
1624          * The raw click event for the entire grid.
1625          * @param {Roo.EventObject} e
1626          */
1627         "click" : true
1628     });
1629 };
1630
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1632     
1633     href : false,
1634     html : false,
1635     preventDefault: true,
1636     
1637     getAutoCreate : function(){
1638         var cfg= {
1639             tag: 'li',
1640         cls: 'dropdown-menu-item',
1641             cn: [
1642             {
1643                 tag : 'a',
1644                 href : '#',
1645                 html : 'Link'
1646             }
1647             ]
1648     };
1649         
1650         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1652         return cfg;
1653     },
1654     
1655     initEvents: function() {
1656         
1657         //this.el.select('a').on('click', this.onClick, this);
1658         
1659     },
1660     onClick : function(e)
1661     {
1662         Roo.log('item on click ');
1663         //if(this.preventDefault){
1664         //    e.preventDefault();
1665         //}
1666         //this.parent().hideMenuItems();
1667         
1668         this.fireEvent('click', this, e);
1669     },
1670     getEl : function()
1671     {
1672         return this.el;
1673     }
1674 });
1675
1676  
1677
1678  /*
1679  * - LGPL
1680  *
1681  * menu separator
1682  * 
1683  */
1684
1685
1686 /**
1687  * @class Roo.bootstrap.MenuSeparator
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap MenuSeparator class
1690  * 
1691  * @constructor
1692  * Create a new MenuItem
1693  * @param {Object} config The config object
1694  */
1695
1696
1697 Roo.bootstrap.MenuSeparator = function(config){
1698     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1699 };
1700
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1702     
1703     getAutoCreate : function(){
1704         var cfg = {
1705             cls: 'divider',
1706             tag : 'li'
1707         };
1708         
1709         return cfg;
1710     }
1711    
1712 });
1713
1714  
1715
1716  
1717 /*
1718 <div class="modal fade">
1719   <div class="modal-dialog">
1720     <div class="modal-content">
1721       <div class="modal-header">
1722         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1723         <h4 class="modal-title">Modal title</h4>
1724       </div>
1725       <div class="modal-body">
1726         <p>One fine body&hellip;</p>
1727       </div>
1728       <div class="modal-footer">
1729         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730         <button type="button" class="btn btn-primary">Save changes</button>
1731       </div>
1732     </div><!-- /.modal-content -->
1733   </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1735 */
1736 /*
1737  * - LGPL
1738  *
1739  * page contgainer.
1740  * 
1741  */
1742
1743 /**
1744  * @class Roo.bootstrap.Modal
1745  * @extends Roo.bootstrap.Component
1746  * Bootstrap Modal class
1747  * @cfg {String} title Title of dialog
1748  * @cfg {Array} buttons Array of buttons or standard button set..
1749  * 
1750  * @constructor
1751  * Create a new Modal Dialog
1752  * @param {Object} config The config object
1753  */
1754
1755 Roo.bootstrap.Modal = function(config){
1756     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1757     this.addEvents({
1758         // raw events
1759         /**
1760          * @event btnclick
1761          * The raw btnclick event for the button
1762          * @param {Roo.EventObject} e
1763          */
1764         "btnclick" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1769     
1770     title : 'test dialog',
1771    
1772     buttons : false,
1773     
1774     onRender : function(ct, position)
1775     {
1776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1777      
1778         if(!this.el){
1779             var cfg = Roo.apply({},  this.getAutoCreate());
1780             cfg.id = Roo.id();
1781             //if(!cfg.name){
1782             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1783             //}
1784             //if (!cfg.name.length) {
1785             //    delete cfg.name;
1786            // }
1787             if (this.cls) {
1788                 cfg.cls += ' ' + this.cls;
1789             }
1790             if (this.style) {
1791                 cfg.style = this.style;
1792             }
1793             this.el = Roo.get(document.body).createChild(cfg, position);
1794         }
1795         //var type = this.el.dom.type;
1796         
1797         if(this.tabIndex !== undefined){
1798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1799         }
1800         
1801         
1802         
1803         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804         this.maskEl.enableDisplayMode("block");
1805         this.maskEl.hide();
1806         //this.el.addClass("x-dlg-modal");
1807     
1808         if (this.buttons) {
1809             Roo.each(this.buttons, function(bb) {
1810                 b = Roo.apply({}, bb);
1811                 b.xns = b.xns || Roo.bootstrap;
1812                 b.xtype = b.xtype || 'Button';
1813                 if (typeof(b.listeners) == 'undefined') {
1814                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1815                 }
1816                 
1817                 var btn = Roo.factory(b);
1818                 
1819                 btn.onRender(this.el.select('.modal-footer').first());
1820                 
1821             },this);
1822         }
1823         // render the children.
1824         var nitems = [];
1825         
1826         if(typeof(this.items) != 'undefined'){
1827             var items = this.items;
1828             delete this.items;
1829
1830             for(var i =0;i < items.length;i++) {
1831                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1832             }
1833         }
1834         
1835         this.items = nitems;
1836         this.initEvents();
1837         //this.el.addClass([this.fieldClass, this.cls]);
1838         
1839     },
1840     getAutoCreate : function(){
1841         
1842         
1843         var bdy = {
1844                 cls : 'modal-body',
1845                 html : this.html || ''
1846         };
1847         
1848          
1849         return modal = {
1850             cls: "modal fade",
1851             cn : [
1852                 {
1853                     cls: "modal-dialog",
1854                     cn : [
1855                         {
1856                             cls : "modal-content",
1857                             cn : [
1858                                 {
1859                                     cls : 'modal-header',
1860                                     cn : [
1861                                         {
1862                                             tag: 'button',
1863                                             cls : 'close',
1864                                             html : '&times'
1865                                         },
1866                                         {
1867                                             tag: 'h4',
1868                                             cls : 'modal-title',
1869                                             html : this.title
1870                                         }
1871                                     
1872                                     ]
1873                                 },
1874                                 bdy,
1875                                 {
1876                                     cls : 'modal-footer' 
1877                                 }
1878                                 
1879                                 
1880                             ]
1881                             
1882                         }
1883                     ]
1884                         
1885                 }
1886             ]
1887             
1888             
1889         };
1890           
1891     },
1892     getChildContainer : function() {
1893          
1894          return this.el.select('.modal-body',true).first();
1895         
1896     },
1897     getButtonContainer : function() {
1898          return this.el.select('.modal-footer',true).first();
1899         
1900     },
1901     initEvents : function()
1902     {
1903         this.el.select('.modal-header .close').on('click', this.hide, this);
1904 //        
1905 //        this.addxtype(this);
1906     },
1907     show : function() {
1908         
1909         if (!this.rendered) {
1910             this.render();
1911         }
1912        
1913         this.el.addClass('on');
1914         this.el.removeClass('fade');
1915         this.el.setStyle('display', 'block');
1916         Roo.get(document.body).addClass("x-body-masked");
1917         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1918         this.maskEl.show();
1919         this.el.setStyle('zIndex', '10001');
1920         this.fireEvent('show', this);
1921         
1922         
1923     },
1924     hide : function()
1925     {
1926         Roo.log('Modal hide?!');
1927         this.maskEl.hide();
1928         Roo.get(document.body).removeClass("x-body-masked");
1929         this.el.removeClass('on');
1930         this.el.addClass('fade');
1931         this.el.setStyle('display', 'none');
1932         this.fireEvent('hide', this);
1933     },
1934     onButtonClick: function(btn,e)
1935     {
1936         //Roo.log([a,b,c]);
1937         this.fireEvent('btnclick', btn.name, e);
1938     }
1939 });
1940
1941
1942 Roo.apply(Roo.bootstrap.Modal,  {
1943     /**
1944          * Button config that displays a single OK button
1945          * @type Object
1946          */
1947         OK :  [{
1948             name : 'ok',
1949             weight : 'primary',
1950             html : 'OK'
1951         }], 
1952         /**
1953          * Button config that displays Yes and No buttons
1954          * @type Object
1955          */
1956         YESNO : [
1957             {
1958                 name  : 'no',
1959                 html : 'No'
1960             },
1961             {
1962                 name  :'yes',
1963                 weight : 'primary',
1964                 html : 'Yes'
1965             }
1966         ],
1967         
1968         /**
1969          * Button config that displays OK and Cancel buttons
1970          * @type Object
1971          */
1972         OKCANCEL : [
1973             {
1974                name : 'cancel',
1975                 html : 'Cancel'
1976             },
1977             {
1978                 name : 'ok',
1979                 weight : 'primary',
1980                 html : 'OK'
1981             }
1982         ],
1983         /**
1984          * Button config that displays Yes, No and Cancel buttons
1985          * @type Object
1986          */
1987         YESNOCANCEL : [
1988             {
1989                 name : 'yes',
1990                 weight : 'primary',
1991                 html : 'Yes'
1992             },
1993             {
1994                 name : 'no',
1995                 html : 'No'
1996             },
1997             {
1998                 name : 'cancel',
1999                 html : 'Cancel'
2000             }
2001         ]
2002 });
2003  /*
2004  * - LGPL
2005  *
2006  * navbar
2007  * 
2008  */
2009
2010 /**
2011  * @class Roo.bootstrap.Navbar
2012  * @extends Roo.bootstrap.Component
2013  * Bootstrap Navbar class
2014  * @cfg {Boolean} sidebar has side bar
2015  * @cfg {Boolean} bar is a bar?
2016  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017  * @cfg {String} brand what is brand
2018  * @cfg {Boolean} inverse is inverted color
2019  * @cfg {String} type (nav | pills | tabs)
2020  * @cfg {Boolean} arrangement stacked | justified
2021  * @cfg {String} align (left | right) alignment
2022  * @cfg {String} brand_href href of the brand
2023  * @cfg {Boolean} main (true|false) main nav bar? default false
2024  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2025  *
2026  * 
2027  * @constructor
2028  * Create a new Navbar
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.Navbar = function(config){
2034     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2038     
2039     sidebar: false,
2040     
2041     bar: false,
2042     brand: '',
2043     inverse: false,
2044     position: '',
2045     align : false,
2046     type: 'nav',
2047     arrangement: '',
2048     brand_href: false,
2049     main : false,
2050     loadMask : false,
2051     
2052     getAutoCreate : function(){
2053         var cfg = {
2054             cls : 'navbar'
2055         };
2056         
2057         if (this.sidebar === true) {
2058             cfg = {
2059                 tag: 'div',
2060                 cls: 'sidebar-nav'
2061             };
2062             return cfg;
2063         }
2064         
2065         if (this.bar === true) {
2066             cfg = {
2067                 tag: 'nav',
2068                 cls: 'navbar',
2069                 role: 'navigation',
2070                 cn: [
2071                     {
2072                         tag: 'div',
2073                         cls: 'navbar-header',
2074                         cn: [
2075                             {
2076                             tag: 'button',
2077                             type: 'button',
2078                             cls: 'navbar-toggle',
2079                             'data-toggle': 'collapse',
2080                             cn: [
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'sr-only',
2084                                     html: 'Toggle navigation'
2085                                 },
2086                                 {
2087                                     tag: 'span',
2088                                     cls: 'icon-bar'
2089                                 },
2090                                 {
2091                                     tag: 'span',
2092                                     cls: 'icon-bar'
2093                                 },
2094                                 {
2095                                     tag: 'span',
2096                                     cls: 'icon-bar'
2097                                 }
2098                             ]
2099                             }
2100                         ]
2101                     },
2102                     {
2103                     tag: 'div',
2104                     cls: 'collapse navbar-collapse'
2105                     }
2106                 ]
2107             };
2108             
2109             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2110             
2111             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112                 cfg.cls += ' navbar-' + this.position;
2113                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2114             }
2115             
2116             if (this.brand !== '') {
2117                 cfg.cn[0].cn.push({
2118                     tag: 'a',
2119                     href: this.brand_href ? this.brand_href : '#',
2120                     cls: 'navbar-brand',
2121                     cn: [
2122                     this.brand
2123                     ]
2124                 });
2125             }
2126             
2127             if(this.main){
2128                 cfg.cls += ' main-nav';
2129             }
2130             
2131             
2132             return cfg;
2133         
2134         } else if (this.bar === false) {
2135             
2136         } else {
2137             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2138         }
2139         
2140         cfg.cn = [
2141             {
2142                 cls: 'nav',
2143                 tag : 'ul'
2144             }
2145         ];
2146         
2147         if (['tabs','pills'].indexOf(this.type)!==-1) {
2148             cfg.cn[0].cls += ' nav-' + this.type
2149         } else {
2150             if (this.type!=='nav') {
2151             Roo.log('nav type must be nav/tabs/pills')
2152             }
2153             cfg.cn[0].cls += ' navbar-nav'
2154         }
2155         
2156         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157             cfg.cn[0].cls += ' nav-' + this.arrangement;
2158         }
2159         
2160         if (this.align === 'right') {
2161             cfg.cn[0].cls += ' navbar-right';
2162         }
2163         if (this.inverse) {
2164             cfg.cls += ' navbar-inverse';
2165             
2166         }
2167         
2168         
2169         return cfg;
2170     },
2171     
2172     initEvents :function ()
2173     {
2174         //Roo.log(this.el.select('.navbar-toggle',true));
2175         this.el.select('.navbar-toggle',true).on('click', function() {
2176            // Roo.log('click');
2177             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2178         }, this);
2179         
2180         var mark = {
2181             tag: "div",
2182             cls:"x-dlg-mask"
2183         }
2184         
2185         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2186         
2187         var size = this.el.getSize();
2188         this.maskEl.setSize(size.width, size.height);
2189         this.maskEl.enableDisplayMode("block");
2190         this.maskEl.hide();
2191         
2192         if(this.loadMask){
2193             this.maskEl.show();
2194         }
2195     },
2196     
2197     
2198     getChildContainer : function()
2199     {
2200         if (this.bar === true) {
2201             return this.el.select('.collapse',true).first();
2202         }
2203         
2204         return this.el;
2205     },
2206     
2207     mask : function()
2208     {
2209         this.maskEl.show();
2210     },
2211     
2212     unmask : function()
2213     {
2214         this.maskEl.hide();
2215     }
2216    
2217 });
2218
2219  
2220
2221  /*
2222  * - LGPL
2223  *
2224  * nav group
2225  * 
2226  */
2227
2228 /**
2229  * @class Roo.bootstrap.NavGroup
2230  * @extends Roo.bootstrap.Component
2231  * Bootstrap NavGroup class
2232  * @cfg {String} align left | right
2233  * @cfg {Boolean} inverse false | true
2234  * @cfg {String} type (nav|pills|tab) default nav
2235  * 
2236  * @constructor
2237  * Create a new nav group
2238  * @param {Object} config The config object
2239  */
2240
2241 Roo.bootstrap.NavGroup = function(config){
2242     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2243 };
2244
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2246     
2247     align: '',
2248     inverse: false,
2249     form: false,
2250     type: 'nav',
2251     
2252     getAutoCreate : function(){
2253         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2254         
2255         cfg = {
2256             tag : 'ul',
2257             cls: 'nav' 
2258         }
2259         
2260         if (['tabs','pills'].indexOf(this.type)!==-1) {
2261             cfg.cls += ' nav-' + this.type
2262         } else {
2263             if (this.type!=='nav') {
2264                 Roo.log('nav type must be nav/tabs/pills')
2265             }
2266             cfg.cls += ' navbar-nav'
2267         }
2268         
2269         if (this.parent().sidebar === true) {
2270             cfg = {
2271                 tag: 'ul',
2272                 cls: 'dashboard-menu'
2273             }
2274             
2275             return cfg;
2276         }
2277         
2278         if (this.form === true) {
2279             cfg = {
2280                 tag: 'form',
2281                 cls: 'navbar-form'
2282             }
2283             
2284             if (this.align === 'right') {
2285                 cfg.cls += ' navbar-right';
2286             } else {
2287                 cfg.cls += ' navbar-left';
2288             }
2289         }
2290         
2291         if (this.align === 'right') {
2292             cfg.cls += ' navbar-right';
2293         }
2294         
2295         if (this.inverse) {
2296             cfg.cls += ' navbar-inverse';
2297             
2298         }
2299         
2300         
2301         return cfg;
2302     }
2303    
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * row
2312  * 
2313  */
2314
2315 /**
2316  * @class Roo.bootstrap.Navbar.Item
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap Navbar.Button class
2319  * @cfg {String} href  link to
2320  * @cfg {String} html content of button
2321  * @cfg {String} badge text inside badge
2322  * @cfg {String} glyphicon name of glyphicon
2323  * @cfg {String} icon name of font awesome icon
2324  * @cfg {Boolena} active Is item active
2325  * @cfg {Boolean} preventDefault (true | false) default false
2326   
2327  * @constructor
2328  * Create a new Navbar Button
2329  * @param {Object} config The config object
2330  */
2331 Roo.bootstrap.Navbar.Item = function(config){
2332     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2333     this.addEvents({
2334         // raw events
2335         /**
2336          * @event click
2337          * The raw click event for the entire grid.
2338          * @param {Roo.EventObject} e
2339          */
2340         "click" : true
2341     });
2342 };
2343
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2345     
2346     href: false,
2347     html: '',
2348     badge: '',
2349     icon: false,
2350     glyphicon: false,
2351     icon: false,
2352     active: false,
2353     preventDefault : false,
2354     
2355     getAutoCreate : function(){
2356         
2357         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2358         
2359         if (this.parent().parent().sidebar === true) {
2360             cfg = {
2361                 tag: 'li',
2362                 cls: '',
2363                 cn: [
2364                     {
2365                         tag: 'p',
2366                         cls: ''
2367                     }
2368                 ]
2369             }
2370             
2371             if (this.html) {
2372                 cfg.cn[0].html = this.html;
2373             }
2374             
2375             if (this.active) {
2376                 this.cls += ' active';
2377             }
2378             
2379             if (this.menu) {
2380                 cfg.cn[0].cls += ' dropdown-toggle';
2381                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2382             }
2383             
2384             if (this.href) {
2385                 cfg.cn[0].tag = 'a',
2386                 cfg.cn[0].href = this.href;
2387             }
2388             
2389             if (this.glyphicon) {
2390                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2391             }
2392             
2393             if (this.icon) {
2394                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2395             }
2396             
2397             return cfg;
2398         }
2399         
2400         cfg = {
2401             tag: 'li',
2402             cls: 'nav-item'
2403         }
2404         
2405         if (this.active) {
2406             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2407         }
2408             
2409         cfg.cn = [
2410             {
2411                 tag: 'p',
2412                 html: 'Text'
2413             }
2414         ];
2415         
2416         if (this.glyphicon) {
2417             if(cfg.html){cfg.html = ' ' + this.html};
2418             cfg.cn=[
2419                 {
2420                     tag: 'span',
2421                     cls: 'glyphicon glyphicon-' + this.glyphicon
2422                 }
2423             ];
2424         }
2425         
2426         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2427         
2428         if (this.menu) {
2429             cfg.cn[0].tag='a';
2430             cfg.cn[0].href='#';
2431             cfg.cn[0].html += " <span class='caret'></span>";
2432         //}else if (!this.href) {
2433         //    cfg.cn[0].tag='p';
2434         //    cfg.cn[0].cls='navbar-text';
2435         } else {
2436             cfg.cn[0].tag='a';
2437             cfg.cn[0].href=this.href||'#';
2438             cfg.cn[0].html=this.html;
2439         }
2440         
2441         if (this.badge !== '') {
2442             
2443             cfg.cn[0].cn=[
2444                 cfg.cn[0].html + ' ',
2445                 {
2446                     tag: 'span',
2447                     cls: 'badge',
2448                     html: this.badge
2449                 }
2450             ];
2451             cfg.cn[0].html=''
2452         }
2453          
2454         if (this.icon) {
2455             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2456         }
2457         
2458         return cfg;
2459     },
2460     initEvents: function() {
2461        // Roo.log('init events?');
2462        // Roo.log(this.el.dom);
2463         this.el.select('a',true).on('click', this.onClick, this);
2464     },
2465     
2466     onClick : function(e)
2467     {
2468         if(this.preventDefault){
2469             e.preventDefault();
2470         }
2471         
2472         if(this.fireEvent('click', this, e) === false){
2473             return;
2474         };
2475         
2476         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477             this.onTabsClick(e);
2478         } 
2479     },
2480     
2481     onTabsClick : function(e)
2482     {
2483         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484             v.removeClass('active');
2485         })
2486
2487         this.el.addClass('active');
2488
2489         if(this.href && this.href.substring(0,1) == '#'){
2490             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2491
2492             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493                 v.removeClass('active');
2494             });
2495
2496             tab.addClass('active');
2497         }
2498     }
2499    
2500 });
2501  
2502
2503  /*
2504  * - LGPL
2505  *
2506  * row
2507  * 
2508  */
2509
2510 /**
2511  * @class Roo.bootstrap.Row
2512  * @extends Roo.bootstrap.Component
2513  * Bootstrap Row class (contains columns...)
2514  * 
2515  * @constructor
2516  * Create a new Row
2517  * @param {Object} config The config object
2518  */
2519
2520 Roo.bootstrap.Row = function(config){
2521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2525     
2526     getAutoCreate : function(){
2527        return {
2528             cls: 'row clearfix'
2529        };
2530     }
2531     
2532     
2533 });
2534
2535  
2536
2537  /*
2538  * - LGPL
2539  *
2540  * element
2541  * 
2542  */
2543
2544 /**
2545  * @class Roo.bootstrap.Element
2546  * @extends Roo.bootstrap.Component
2547  * Bootstrap Element class
2548  * @cfg {String} html contents of the element
2549  * @cfg {String} tag tag of the element
2550  * @cfg {String} cls class of the element
2551  * 
2552  * @constructor
2553  * Create a new Element
2554  * @param {Object} config The config object
2555  */
2556
2557 Roo.bootstrap.Element = function(config){
2558     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2559 };
2560
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2562     
2563     tag: 'div',
2564     cls: '',
2565     html: '',
2566      
2567     
2568     getAutoCreate : function(){
2569         
2570         var cfg = {
2571             tag: this.tag,
2572             cls: this.cls,
2573             html: this.html
2574         }
2575         
2576         
2577         
2578         return cfg;
2579     }
2580    
2581 });
2582
2583  
2584
2585  /*
2586  * - LGPL
2587  *
2588  * pagination
2589  * 
2590  */
2591
2592 /**
2593  * @class Roo.bootstrap.Pagination
2594  * @extends Roo.bootstrap.Component
2595  * Bootstrap Pagination class
2596  * @cfg {String} size xs | sm | md | lg
2597  * @cfg {Boolean} inverse false | true
2598  * 
2599  * @constructor
2600  * Create a new Pagination
2601  * @param {Object} config The config object
2602  */
2603
2604 Roo.bootstrap.Pagination = function(config){
2605     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2609     
2610     cls: false,
2611     size: false,
2612     inverse: false,
2613     
2614     getAutoCreate : function(){
2615         var cfg = {
2616             tag: 'ul',
2617                 cls: 'pagination'
2618         };
2619         if (this.inverse) {
2620             cfg.cls += ' inverse';
2621         }
2622         if (this.html) {
2623             cfg.html=this.html;
2624         }
2625         if (this.cls) {
2626             cfg.cls += " " + this.cls;
2627         }
2628         return cfg;
2629     }
2630    
2631 });
2632
2633  
2634
2635  /*
2636  * - LGPL
2637  *
2638  * Pagination item
2639  * 
2640  */
2641
2642
2643 /**
2644  * @class Roo.bootstrap.PaginationItem
2645  * @extends Roo.bootstrap.Component
2646  * Bootstrap PaginationItem class
2647  * @cfg {String} html text
2648  * @cfg {String} href the link
2649  * @cfg {Boolean} preventDefault (true | false) default true
2650  * @cfg {Boolean} active (true | false) default false
2651  * 
2652  * 
2653  * @constructor
2654  * Create a new PaginationItem
2655  * @param {Object} config The config object
2656  */
2657
2658
2659 Roo.bootstrap.PaginationItem = function(config){
2660     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2661     this.addEvents({
2662         // raw events
2663         /**
2664          * @event click
2665          * The raw click event for the entire grid.
2666          * @param {Roo.EventObject} e
2667          */
2668         "click" : true
2669     });
2670 };
2671
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2673     
2674     href : false,
2675     html : false,
2676     preventDefault: true,
2677     active : false,
2678     cls : false,
2679     
2680     getAutoCreate : function(){
2681         var cfg= {
2682             tag: 'li',
2683             cn: [
2684                 {
2685                     tag : 'a',
2686                     href : this.href ? this.href : '#',
2687                     html : this.html ? this.html : ''
2688                 }
2689             ]
2690         };
2691         
2692         if(this.cls){
2693             cfg.cls = this.cls;
2694         }
2695         
2696         if(this.active){
2697             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2698         }
2699         
2700         return cfg;
2701     },
2702     
2703     initEvents: function() {
2704         
2705         this.el.on('click', this.onClick, this);
2706         
2707     },
2708     onClick : function(e)
2709     {
2710         Roo.log('PaginationItem on click ');
2711         if(this.preventDefault){
2712             e.preventDefault();
2713         }
2714         
2715         this.fireEvent('click', this, e);
2716     }
2717    
2718 });
2719
2720  
2721
2722  /*
2723  * - LGPL
2724  *
2725  * slider
2726  * 
2727  */
2728
2729
2730 /**
2731  * @class Roo.bootstrap.Slider
2732  * @extends Roo.bootstrap.Component
2733  * Bootstrap Slider class
2734  *    
2735  * @constructor
2736  * Create a new Slider
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.Slider = function(config){
2741     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2745     
2746     getAutoCreate : function(){
2747         
2748         var cfg = {
2749             tag: 'div',
2750             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2751             cn: [
2752                 {
2753                     tag: 'a',
2754                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2755                 }
2756             ]
2757         }
2758         
2759         return cfg;
2760     }
2761    
2762 });
2763
2764  /*
2765  * - LGPL
2766  *
2767  * table
2768  * 
2769  */
2770
2771 /**
2772  * @class Roo.bootstrap.Table
2773  * @extends Roo.bootstrap.Component
2774  * Bootstrap Table class
2775  * @cfg {String} cls table class
2776  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777  * @cfg {String} bgcolor Specifies the background color for a table
2778  * @cfg {Number} border Specifies whether the table cells should have borders or not
2779  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780  * @cfg {Number} cellspacing Specifies the space between cells
2781  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783  * @cfg {String} sortable Specifies that the table should be sortable
2784  * @cfg {String} summary Specifies a summary of the content of a table
2785  * @cfg {Number} width Specifies the width of a table
2786  * 
2787  * @cfg {boolean} striped Should the rows be alternative striped
2788  * @cfg {boolean} bordered Add borders to the table
2789  * @cfg {boolean} hover Add hover highlighting
2790  * @cfg {boolean} condensed Format condensed
2791  * @cfg {boolean} responsive Format condensed
2792  *
2793  
2794  
2795  * 
2796  * @constructor
2797  * Create a new Table
2798  * @param {Object} config The config object
2799  */
2800
2801 Roo.bootstrap.Table = function(config){
2802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2803     
2804     if (this.sm) {
2805         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806         this.sm = this.selModel;
2807         this.sm.xmodule = this.xmodule || false;
2808     }
2809     if (this.cm && typeof(this.cm.config) == 'undefined') {
2810         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811         this.cm = this.colModel;
2812         this.cm.xmodule = this.xmodule || false;
2813     }
2814     if (this.store) {
2815         this.store= Roo.factory(this.store, Roo.data);
2816         this.ds = this.store;
2817         this.ds.xmodule = this.xmodule || false;
2818          
2819     }
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2823     
2824     cls: false,
2825     align: false,
2826     bgcolor: false,
2827     border: false,
2828     cellpadding: false,
2829     cellspacing: false,
2830     frame: false,
2831     rules: false,
2832     sortable: false,
2833     summary: false,
2834     width: false,
2835     striped : false,
2836     bordered: false,
2837     hover:  false,
2838     condensed : false,
2839     responsive : false,
2840     sm : false,
2841     cm : false,
2842     store : false,
2843     
2844     getAutoCreate : function(){
2845         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2846         
2847         cfg = {
2848             tag: 'table',
2849             cls : 'table',
2850             cn : []
2851         }
2852             
2853         if (this.striped) {
2854             cfg.cls += ' table-striped';
2855         }
2856         if (this.hover) {
2857             cfg.cls += ' table-hover';
2858         }
2859         if (this.bordered) {
2860             cfg.cls += ' table-bordered';
2861         }
2862         if (this.condensed) {
2863             cfg.cls += ' table-condensed';
2864         }
2865         if (this.responsive) {
2866             cfg.cls += ' table-responsive';
2867         }
2868         
2869           
2870         
2871         
2872         if (this.cls) {
2873             cfg.cls+=  ' ' +this.cls;
2874         }
2875         
2876         // this lot should be simplifed...
2877         
2878         if (this.align) {
2879             cfg.align=this.align;
2880         }
2881         if (this.bgcolor) {
2882             cfg.bgcolor=this.bgcolor;
2883         }
2884         if (this.border) {
2885             cfg.border=this.border;
2886         }
2887         if (this.cellpadding) {
2888             cfg.cellpadding=this.cellpadding;
2889         }
2890         if (this.cellspacing) {
2891             cfg.cellspacing=this.cellspacing;
2892         }
2893         if (this.frame) {
2894             cfg.frame=this.frame;
2895         }
2896         if (this.rules) {
2897             cfg.rules=this.rules;
2898         }
2899         if (this.sortable) {
2900             cfg.sortable=this.sortable;
2901         }
2902         if (this.summary) {
2903             cfg.summary=this.summary;
2904         }
2905         if (this.width) {
2906             cfg.width=this.width;
2907         }
2908         
2909         if(this.store || this.cm){
2910             cfg.cn.push(this.renderHeader());
2911             cfg.cn.push(this.renderBody());
2912             cfg.cn.push(this.renderFooter());
2913             
2914             cfg.cls+=  ' TableGrid';
2915         }
2916         
2917         return cfg;
2918     },
2919 //    
2920 //    initTableGrid : function()
2921 //    {
2922 //        var cfg = {};
2923 //        
2924 //        var header = {
2925 //            tag: 'thead',
2926 //            cn : []
2927 //        };
2928 //        
2929 //        var cm = this.cm;
2930 //        
2931 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2932 //            header.cn.push({
2933 //                tag: 'th',
2934 //                html: cm.getColumnHeader(i)
2935 //            })
2936 //        }
2937 //        
2938 //        cfg.push(header);
2939 //        
2940 //        return cfg;
2941 //        
2942 //        
2943 //    },
2944     
2945     initEvents : function()
2946     {   
2947         if(!this.store || !this.cm){
2948             return;
2949         }
2950         
2951         Roo.log('initEvents with ds!!!!');
2952         
2953         var _this = this;
2954         
2955         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
2956             e.on('click', _this.sort, _this);
2957         });
2958 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2959 //        this.maskEl.enableDisplayMode("block");
2960 //        this.maskEl.show();
2961         
2962         this.store.on('load', this.onLoad, this);
2963         this.store.on('beforeload', this.onBeforeLoad, this);
2964         
2965         this.store.load();
2966         
2967         
2968         
2969     },
2970     
2971     sort : function(e,el)
2972     {
2973         var col = Roo.get(el)
2974         
2975         if(!col.hasClass('sortable')){
2976             return;
2977         }
2978         
2979         var sort = col.attr('sort');
2980         var dir = 'ASC';
2981         
2982         if(col.hasClass('glyphicon-arrow-up')){
2983             dir = 'DESC';
2984         }
2985         
2986         this.store.sortInfo = {field : sort, direction : dir};
2987         
2988         this.store.load();
2989     },
2990     
2991     renderHeader : function()
2992     {
2993         var header = {
2994             tag: 'thead',
2995             cn : []
2996         };
2997         
2998         var cm = this.cm;
2999         
3000         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3001             
3002             var config = cm.config[i];
3003             
3004             var c = {
3005                 tag: 'th',
3006                 html: cm.getColumnHeader(i)
3007             };
3008             
3009             if(typeof(config.dataIndex) != 'undefined'){
3010                 c.sort = config.dataIndex;
3011             }
3012             
3013             if(typeof(config.sortable) != 'undefined' && config.sortable){
3014                 c.cls = 'sortable';
3015             }
3016             
3017             header.cn.push(c)
3018         }
3019         
3020         return header;
3021     },
3022     
3023     renderBody : function()
3024     {
3025         var body = {
3026             tag: 'tbody',
3027             cn : []
3028         };
3029         
3030         return body;
3031     },
3032     
3033     renderFooter : function()
3034     {
3035         var footer = {
3036             tag: 'tfoot',
3037             cn : []
3038         };
3039         
3040         return footer;
3041     },
3042     
3043     onLoad : function()
3044     {
3045         Roo.log('ds onload');
3046         
3047         var _this = this;
3048         var cm = this.cm;
3049         
3050         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3051             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3052             
3053             var sortable = e.attr('')
3054             
3055             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.dir.toUpperCase() == 'ASC'){
3056                 e.addClass('glyphicon', 'glyphicon-arrow-up');
3057                 return;
3058             }
3059             
3060             e.addClass('glyphicon', 'glyphicon-arrow-down');
3061         });
3062         
3063         var tbody = this.el.select('tbody', true).first();
3064         
3065         var renders = [];
3066         
3067         if(this.store.getCount() > 0){
3068             this.store.data.each(function(d){
3069                 var row = {
3070                     tag : 'tr',
3071                     cn : []
3072                 };
3073                 
3074                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3075                     var renderer = cm.getRenderer(i);
3076                     var config = cm.config[i];
3077                     var value = '';
3078                     var id = Roo.id();
3079                     
3080                     if(typeof(renderer) !== 'undefined'){
3081                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3082                     }
3083                     
3084                     if(typeof(value) === 'object'){
3085                         renders.push({
3086                             id : id,
3087                             cfg : value 
3088                         })
3089                     }
3090                     
3091                     var td = {
3092                         tag: 'td',
3093                         id: id,
3094                         html: (typeof(value) === 'object') ? '' : value
3095                     };
3096                     
3097                     if(typeof(config.width) != 'undefined'){
3098                         td.width = config.width;
3099                     }
3100                     
3101                     row.cn.push(td);
3102                    
3103                 }
3104                 
3105                 tbody.createChild(row);
3106                 
3107             });
3108         }
3109         
3110         
3111         if(renders.length){
3112             var _this = this;
3113             Roo.each(renders, function(r){
3114                 _this.renderColumn(r);
3115             })
3116         }
3117 //        
3118 //        if(this.loadMask){
3119 //            this.maskEl.hide();
3120 //        }
3121     },
3122     
3123     onBeforeLoad : function()
3124     {
3125         Roo.log('ds onBeforeLoad');
3126         
3127         this.clear();
3128         
3129 //        if(this.loadMask){
3130 //            this.maskEl.show();
3131 //        }
3132     },
3133     
3134     clear : function()
3135     {
3136         this.el.select('tbody', true).first().dom.innerHTML = '';
3137     },
3138     
3139     getSelectionModel : function(){
3140         if(!this.selModel){
3141             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3142         }
3143         return this.selModel;
3144     },
3145     
3146     renderColumn : function(r)
3147     {
3148         var _this = this;
3149         r.cfg.render(Roo.get(r.id));
3150         
3151         if(r.cfg.cn){
3152             Roo.each(r.cfg.cn, function(c){
3153                 var child = {
3154                     id: r.id,
3155                     cfg: c
3156                 }
3157                 _this.renderColumn(child);
3158             })
3159         }
3160     }
3161    
3162 });
3163
3164  
3165
3166  /*
3167  * - LGPL
3168  *
3169  * table cell
3170  * 
3171  */
3172
3173 /**
3174  * @class Roo.bootstrap.TableCell
3175  * @extends Roo.bootstrap.Component
3176  * Bootstrap TableCell class
3177  * @cfg {String} html cell contain text
3178  * @cfg {String} cls cell class
3179  * @cfg {String} tag cell tag (td|th) default td
3180  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3181  * @cfg {String} align Aligns the content in a cell
3182  * @cfg {String} axis Categorizes cells
3183  * @cfg {String} bgcolor Specifies the background color of a cell
3184  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3185  * @cfg {Number} colspan Specifies the number of columns a cell should span
3186  * @cfg {String} headers Specifies one or more header cells a cell is related to
3187  * @cfg {Number} height Sets the height of a cell
3188  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3189  * @cfg {Number} rowspan Sets the number of rows a cell should span
3190  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3191  * @cfg {String} valign Vertical aligns the content in a cell
3192  * @cfg {Number} width Specifies the width of a cell
3193  * 
3194  * @constructor
3195  * Create a new TableCell
3196  * @param {Object} config The config object
3197  */
3198
3199 Roo.bootstrap.TableCell = function(config){
3200     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3201 };
3202
3203 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3204     
3205     html: false,
3206     cls: false,
3207     tag: false,
3208     abbr: false,
3209     align: false,
3210     axis: false,
3211     bgcolor: false,
3212     charoff: false,
3213     colspan: false,
3214     headers: false,
3215     height: false,
3216     nowrap: false,
3217     rowspan: false,
3218     scope: false,
3219     valign: false,
3220     width: false,
3221     
3222     
3223     getAutoCreate : function(){
3224         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3225         
3226         cfg = {
3227             tag: 'td'
3228         }
3229         
3230         if(this.tag){
3231             cfg.tag = this.tag;
3232         }
3233         
3234         if (this.html) {
3235             cfg.html=this.html
3236         }
3237         if (this.cls) {
3238             cfg.cls=this.cls
3239         }
3240         if (this.abbr) {
3241             cfg.abbr=this.abbr
3242         }
3243         if (this.align) {
3244             cfg.align=this.align
3245         }
3246         if (this.axis) {
3247             cfg.axis=this.axis
3248         }
3249         if (this.bgcolor) {
3250             cfg.bgcolor=this.bgcolor
3251         }
3252         if (this.charoff) {
3253             cfg.charoff=this.charoff
3254         }
3255         if (this.colspan) {
3256             cfg.colspan=this.colspan
3257         }
3258         if (this.headers) {
3259             cfg.headers=this.headers
3260         }
3261         if (this.height) {
3262             cfg.height=this.height
3263         }
3264         if (this.nowrap) {
3265             cfg.nowrap=this.nowrap
3266         }
3267         if (this.rowspan) {
3268             cfg.rowspan=this.rowspan
3269         }
3270         if (this.scope) {
3271             cfg.scope=this.scope
3272         }
3273         if (this.valign) {
3274             cfg.valign=this.valign
3275         }
3276         if (this.width) {
3277             cfg.width=this.width
3278         }
3279         
3280         
3281         return cfg;
3282     }
3283    
3284 });
3285
3286  
3287
3288  /*
3289  * - LGPL
3290  *
3291  * table row
3292  * 
3293  */
3294
3295 /**
3296  * @class Roo.bootstrap.TableRow
3297  * @extends Roo.bootstrap.Component
3298  * Bootstrap TableRow class
3299  * @cfg {String} cls row class
3300  * @cfg {String} align Aligns the content in a table row
3301  * @cfg {String} bgcolor Specifies a background color for a table row
3302  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3303  * @cfg {String} valign Vertical aligns the content in a table row
3304  * 
3305  * @constructor
3306  * Create a new TableRow
3307  * @param {Object} config The config object
3308  */
3309
3310 Roo.bootstrap.TableRow = function(config){
3311     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3312 };
3313
3314 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3315     
3316     cls: false,
3317     align: false,
3318     bgcolor: false,
3319     charoff: false,
3320     valign: false,
3321     
3322     getAutoCreate : function(){
3323         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3324         
3325         cfg = {
3326             tag: 'tr'
3327         }
3328             
3329         if(this.cls){
3330             cfg.cls = this.cls;
3331         }
3332         if(this.align){
3333             cfg.align = this.align;
3334         }
3335         if(this.bgcolor){
3336             cfg.bgcolor = this.bgcolor;
3337         }
3338         if(this.charoff){
3339             cfg.charoff = this.charoff;
3340         }
3341         if(this.valign){
3342             cfg.valign = this.valign;
3343         }
3344         
3345         return cfg;
3346     }
3347    
3348 });
3349
3350  
3351
3352  /*
3353  * - LGPL
3354  *
3355  * table body
3356  * 
3357  */
3358
3359 /**
3360  * @class Roo.bootstrap.TableBody
3361  * @extends Roo.bootstrap.Component
3362  * Bootstrap TableBody class
3363  * @cfg {String} cls element class
3364  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3365  * @cfg {String} align Aligns the content inside the element
3366  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3367  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3368  * 
3369  * @constructor
3370  * Create a new TableBody
3371  * @param {Object} config The config object
3372  */
3373
3374 Roo.bootstrap.TableBody = function(config){
3375     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3376 };
3377
3378 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3379     
3380     cls: false,
3381     tag: false,
3382     align: false,
3383     charoff: false,
3384     valign: false,
3385     
3386     getAutoCreate : function(){
3387         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3388         
3389         cfg = {
3390             tag: 'tbody'
3391         }
3392             
3393         if (this.cls) {
3394             cfg.cls=this.cls
3395         }
3396         if(this.tag){
3397             cfg.tag = this.tag;
3398         }
3399         
3400         if(this.align){
3401             cfg.align = this.align;
3402         }
3403         if(this.charoff){
3404             cfg.charoff = this.charoff;
3405         }
3406         if(this.valign){
3407             cfg.valign = this.valign;
3408         }
3409         
3410         return cfg;
3411     }
3412     
3413     
3414 //    initEvents : function()
3415 //    {
3416 //        
3417 //        if(!this.store){
3418 //            return;
3419 //        }
3420 //        
3421 //        this.store = Roo.factory(this.store, Roo.data);
3422 //        this.store.on('load', this.onLoad, this);
3423 //        
3424 //        this.store.load();
3425 //        
3426 //    },
3427 //    
3428 //    onLoad: function () 
3429 //    {   
3430 //        this.fireEvent('load', this);
3431 //    }
3432 //    
3433 //   
3434 });
3435
3436  
3437
3438  /*
3439  * Based on:
3440  * Ext JS Library 1.1.1
3441  * Copyright(c) 2006-2007, Ext JS, LLC.
3442  *
3443  * Originally Released Under LGPL - original licence link has changed is not relivant.
3444  *
3445  * Fork - LGPL
3446  * <script type="text/javascript">
3447  */
3448
3449 // as we use this in bootstrap.
3450 Roo.namespace('Roo.form');
3451  /**
3452  * @class Roo.form.Action
3453  * Internal Class used to handle form actions
3454  * @constructor
3455  * @param {Roo.form.BasicForm} el The form element or its id
3456  * @param {Object} config Configuration options
3457  */
3458
3459  
3460  
3461 // define the action interface
3462 Roo.form.Action = function(form, options){
3463     this.form = form;
3464     this.options = options || {};
3465 };
3466 /**
3467  * Client Validation Failed
3468  * @const 
3469  */
3470 Roo.form.Action.CLIENT_INVALID = 'client';
3471 /**
3472  * Server Validation Failed
3473  * @const 
3474  */
3475 Roo.form.Action.SERVER_INVALID = 'server';
3476  /**
3477  * Connect to Server Failed
3478  * @const 
3479  */
3480 Roo.form.Action.CONNECT_FAILURE = 'connect';
3481 /**
3482  * Reading Data from Server Failed
3483  * @const 
3484  */
3485 Roo.form.Action.LOAD_FAILURE = 'load';
3486
3487 Roo.form.Action.prototype = {
3488     type : 'default',
3489     failureType : undefined,
3490     response : undefined,
3491     result : undefined,
3492
3493     // interface method
3494     run : function(options){
3495
3496     },
3497
3498     // interface method
3499     success : function(response){
3500
3501     },
3502
3503     // interface method
3504     handleResponse : function(response){
3505
3506     },
3507
3508     // default connection failure
3509     failure : function(response){
3510         
3511         this.response = response;
3512         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3513         this.form.afterAction(this, false);
3514     },
3515
3516     processResponse : function(response){
3517         this.response = response;
3518         if(!response.responseText){
3519             return true;
3520         }
3521         this.result = this.handleResponse(response);
3522         return this.result;
3523     },
3524
3525     // utility functions used internally
3526     getUrl : function(appendParams){
3527         var url = this.options.url || this.form.url || this.form.el.dom.action;
3528         if(appendParams){
3529             var p = this.getParams();
3530             if(p){
3531                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3532             }
3533         }
3534         return url;
3535     },
3536
3537     getMethod : function(){
3538         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3539     },
3540
3541     getParams : function(){
3542         var bp = this.form.baseParams;
3543         var p = this.options.params;
3544         if(p){
3545             if(typeof p == "object"){
3546                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3547             }else if(typeof p == 'string' && bp){
3548                 p += '&' + Roo.urlEncode(bp);
3549             }
3550         }else if(bp){
3551             p = Roo.urlEncode(bp);
3552         }
3553         return p;
3554     },
3555
3556     createCallback : function(){
3557         return {
3558             success: this.success,
3559             failure: this.failure,
3560             scope: this,
3561             timeout: (this.form.timeout*1000),
3562             upload: this.form.fileUpload ? this.success : undefined
3563         };
3564     }
3565 };
3566
3567 Roo.form.Action.Submit = function(form, options){
3568     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3569 };
3570
3571 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3572     type : 'submit',
3573
3574     haveProgress : false,
3575     uploadComplete : false,
3576     
3577     // uploadProgress indicator.
3578     uploadProgress : function()
3579     {
3580         if (!this.form.progressUrl) {
3581             return;
3582         }
3583         
3584         if (!this.haveProgress) {
3585             Roo.MessageBox.progress("Uploading", "Uploading");
3586         }
3587         if (this.uploadComplete) {
3588            Roo.MessageBox.hide();
3589            return;
3590         }
3591         
3592         this.haveProgress = true;
3593    
3594         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3595         
3596         var c = new Roo.data.Connection();
3597         c.request({
3598             url : this.form.progressUrl,
3599             params: {
3600                 id : uid
3601             },
3602             method: 'GET',
3603             success : function(req){
3604                //console.log(data);
3605                 var rdata = false;
3606                 var edata;
3607                 try  {
3608                    rdata = Roo.decode(req.responseText)
3609                 } catch (e) {
3610                     Roo.log("Invalid data from server..");
3611                     Roo.log(edata);
3612                     return;
3613                 }
3614                 if (!rdata || !rdata.success) {
3615                     Roo.log(rdata);
3616                     Roo.MessageBox.alert(Roo.encode(rdata));
3617                     return;
3618                 }
3619                 var data = rdata.data;
3620                 
3621                 if (this.uploadComplete) {
3622                    Roo.MessageBox.hide();
3623                    return;
3624                 }
3625                    
3626                 if (data){
3627                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3628                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3629                     );
3630                 }
3631                 this.uploadProgress.defer(2000,this);
3632             },
3633        
3634             failure: function(data) {
3635                 Roo.log('progress url failed ');
3636                 Roo.log(data);
3637             },
3638             scope : this
3639         });
3640            
3641     },
3642     
3643     
3644     run : function()
3645     {
3646         // run get Values on the form, so it syncs any secondary forms.
3647         this.form.getValues();
3648         
3649         var o = this.options;
3650         var method = this.getMethod();
3651         var isPost = method == 'POST';
3652         if(o.clientValidation === false || this.form.isValid()){
3653             
3654             if (this.form.progressUrl) {
3655                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3656                     (new Date() * 1) + '' + Math.random());
3657                     
3658             } 
3659             
3660             
3661             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3662                 form:this.form.el.dom,
3663                 url:this.getUrl(!isPost),
3664                 method: method,
3665                 params:isPost ? this.getParams() : null,
3666                 isUpload: this.form.fileUpload
3667             }));
3668             
3669             this.uploadProgress();
3670
3671         }else if (o.clientValidation !== false){ // client validation failed
3672             this.failureType = Roo.form.Action.CLIENT_INVALID;
3673             this.form.afterAction(this, false);
3674         }
3675     },
3676
3677     success : function(response)
3678     {
3679         this.uploadComplete= true;
3680         if (this.haveProgress) {
3681             Roo.MessageBox.hide();
3682         }
3683         
3684         
3685         var result = this.processResponse(response);
3686         if(result === true || result.success){
3687             this.form.afterAction(this, true);
3688             return;
3689         }
3690         if(result.errors){
3691             this.form.markInvalid(result.errors);
3692             this.failureType = Roo.form.Action.SERVER_INVALID;
3693         }
3694         this.form.afterAction(this, false);
3695     },
3696     failure : function(response)
3697     {
3698         this.uploadComplete= true;
3699         if (this.haveProgress) {
3700             Roo.MessageBox.hide();
3701         }
3702         
3703         this.response = response;
3704         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3705         this.form.afterAction(this, false);
3706     },
3707     
3708     handleResponse : function(response){
3709         if(this.form.errorReader){
3710             var rs = this.form.errorReader.read(response);
3711             var errors = [];
3712             if(rs.records){
3713                 for(var i = 0, len = rs.records.length; i < len; i++) {
3714                     var r = rs.records[i];
3715                     errors[i] = r.data;
3716                 }
3717             }
3718             if(errors.length < 1){
3719                 errors = null;
3720             }
3721             return {
3722                 success : rs.success,
3723                 errors : errors
3724             };
3725         }
3726         var ret = false;
3727         try {
3728             ret = Roo.decode(response.responseText);
3729         } catch (e) {
3730             ret = {
3731                 success: false,
3732                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3733                 errors : []
3734             };
3735         }
3736         return ret;
3737         
3738     }
3739 });
3740
3741
3742 Roo.form.Action.Load = function(form, options){
3743     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3744     this.reader = this.form.reader;
3745 };
3746
3747 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3748     type : 'load',
3749
3750     run : function(){
3751         
3752         Roo.Ajax.request(Roo.apply(
3753                 this.createCallback(), {
3754                     method:this.getMethod(),
3755                     url:this.getUrl(false),
3756                     params:this.getParams()
3757         }));
3758     },
3759
3760     success : function(response){
3761         
3762         var result = this.processResponse(response);
3763         if(result === true || !result.success || !result.data){
3764             this.failureType = Roo.form.Action.LOAD_FAILURE;
3765             this.form.afterAction(this, false);
3766             return;
3767         }
3768         this.form.clearInvalid();
3769         this.form.setValues(result.data);
3770         this.form.afterAction(this, true);
3771     },
3772
3773     handleResponse : function(response){
3774         if(this.form.reader){
3775             var rs = this.form.reader.read(response);
3776             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3777             return {
3778                 success : rs.success,
3779                 data : data
3780             };
3781         }
3782         return Roo.decode(response.responseText);
3783     }
3784 });
3785
3786 Roo.form.Action.ACTION_TYPES = {
3787     'load' : Roo.form.Action.Load,
3788     'submit' : Roo.form.Action.Submit
3789 };/*
3790  * - LGPL
3791  *
3792  * form
3793  * 
3794  */
3795
3796 /**
3797  * @class Roo.bootstrap.Form
3798  * @extends Roo.bootstrap.Component
3799  * Bootstrap Form class
3800  * @cfg {String} method  GET | POST (default POST)
3801  * @cfg {String} labelAlign top | left (default top)
3802   * @cfg {String} align left  | right - for navbars
3803
3804  * 
3805  * @constructor
3806  * Create a new Form
3807  * @param {Object} config The config object
3808  */
3809
3810
3811 Roo.bootstrap.Form = function(config){
3812     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3813     this.addEvents({
3814         /**
3815          * @event clientvalidation
3816          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3817          * @param {Form} this
3818          * @param {Boolean} valid true if the form has passed client-side validation
3819          */
3820         clientvalidation: true,
3821         /**
3822          * @event beforeaction
3823          * Fires before any action is performed. Return false to cancel the action.
3824          * @param {Form} this
3825          * @param {Action} action The action to be performed
3826          */
3827         beforeaction: true,
3828         /**
3829          * @event actionfailed
3830          * Fires when an action fails.
3831          * @param {Form} this
3832          * @param {Action} action The action that failed
3833          */
3834         actionfailed : true,
3835         /**
3836          * @event actioncomplete
3837          * Fires when an action is completed.
3838          * @param {Form} this
3839          * @param {Action} action The action that completed
3840          */
3841         actioncomplete : true
3842     });
3843     
3844 };
3845
3846 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3847       
3848      /**
3849      * @cfg {String} method
3850      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3851      */
3852     method : 'POST',
3853     /**
3854      * @cfg {String} url
3855      * The URL to use for form actions if one isn't supplied in the action options.
3856      */
3857     /**
3858      * @cfg {Boolean} fileUpload
3859      * Set to true if this form is a file upload.
3860      */
3861      
3862     /**
3863      * @cfg {Object} baseParams
3864      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3865      */
3866       
3867     /**
3868      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3869      */
3870     timeout: 30,
3871     /**
3872      * @cfg {Sting} align (left|right) for navbar forms
3873      */
3874     align : 'left',
3875
3876     // private
3877     activeAction : null,
3878  
3879     /**
3880      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3881      * element by passing it or its id or mask the form itself by passing in true.
3882      * @type Mixed
3883      */
3884     waitMsgTarget : false,
3885     
3886      
3887     
3888     /**
3889      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3890      * element by passing it or its id or mask the form itself by passing in true.
3891      * @type Mixed
3892      */
3893     
3894     getAutoCreate : function(){
3895         
3896         var cfg = {
3897             tag: 'form',
3898             method : this.method || 'POST',
3899             id : this.id || Roo.id(),
3900             cls : ''
3901         }
3902         if (this.parent().xtype.match(/^Nav/)) {
3903             cfg.cls = 'navbar-form navbar-' + this.align;
3904             
3905         }
3906         
3907         if (this.labelAlign == 'left' ) {
3908             cfg.cls += ' form-horizontal';
3909         }
3910         
3911         
3912         return cfg;
3913     },
3914     initEvents : function()
3915     {
3916         this.el.on('submit', this.onSubmit, this);
3917         
3918         
3919     },
3920     // private
3921     onSubmit : function(e){
3922         e.stopEvent();
3923     },
3924     
3925      /**
3926      * Returns true if client-side validation on the form is successful.
3927      * @return Boolean
3928      */
3929     isValid : function(){
3930         var items = this.getItems();
3931         var valid = true;
3932         items.each(function(f){
3933            if(!f.validate()){
3934                valid = false;
3935                
3936            }
3937         });
3938         return valid;
3939     },
3940     /**
3941      * Returns true if any fields in this form have changed since their original load.
3942      * @return Boolean
3943      */
3944     isDirty : function(){
3945         var dirty = false;
3946         var items = this.getItems();
3947         items.each(function(f){
3948            if(f.isDirty()){
3949                dirty = true;
3950                return false;
3951            }
3952            return true;
3953         });
3954         return dirty;
3955     },
3956      /**
3957      * Performs a predefined action (submit or load) or custom actions you define on this form.
3958      * @param {String} actionName The name of the action type
3959      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3960      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3961      * accept other config options):
3962      * <pre>
3963 Property          Type             Description
3964 ----------------  ---------------  ----------------------------------------------------------------------------------
3965 url               String           The url for the action (defaults to the form's url)
3966 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3967 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3968 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3969                                    validate the form on the client (defaults to false)
3970      * </pre>
3971      * @return {BasicForm} this
3972      */
3973     doAction : function(action, options){
3974         if(typeof action == 'string'){
3975             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3976         }
3977         if(this.fireEvent('beforeaction', this, action) !== false){
3978             this.beforeAction(action);
3979             action.run.defer(100, action);
3980         }
3981         return this;
3982     },
3983     
3984     // private
3985     beforeAction : function(action){
3986         var o = action.options;
3987         
3988         // not really supported yet.. ??
3989         
3990         //if(this.waitMsgTarget === true){
3991             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3992         //}else if(this.waitMsgTarget){
3993         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3994         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3995         //}else {
3996         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3997        // }
3998          
3999     },
4000
4001     // private
4002     afterAction : function(action, success){
4003         this.activeAction = null;
4004         var o = action.options;
4005         
4006         //if(this.waitMsgTarget === true){
4007             this.el.unmask();
4008         //}else if(this.waitMsgTarget){
4009         //    this.waitMsgTarget.unmask();
4010         //}else{
4011         //    Roo.MessageBox.updateProgress(1);
4012         //    Roo.MessageBox.hide();
4013        // }
4014         // 
4015         if(success){
4016             if(o.reset){
4017                 this.reset();
4018             }
4019             Roo.callback(o.success, o.scope, [this, action]);
4020             this.fireEvent('actioncomplete', this, action);
4021             
4022         }else{
4023             
4024             // failure condition..
4025             // we have a scenario where updates need confirming.
4026             // eg. if a locking scenario exists..
4027             // we look for { errors : { needs_confirm : true }} in the response.
4028             if (
4029                 (typeof(action.result) != 'undefined')  &&
4030                 (typeof(action.result.errors) != 'undefined')  &&
4031                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4032            ){
4033                 var _t = this;
4034                 Roo.log("not supported yet");
4035                  /*
4036                 
4037                 Roo.MessageBox.confirm(
4038                     "Change requires confirmation",
4039                     action.result.errorMsg,
4040                     function(r) {
4041                         if (r != 'yes') {
4042                             return;
4043                         }
4044                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4045                     }
4046                     
4047                 );
4048                 */
4049                 
4050                 
4051                 return;
4052             }
4053             
4054             Roo.callback(o.failure, o.scope, [this, action]);
4055             // show an error message if no failed handler is set..
4056             if (!this.hasListener('actionfailed')) {
4057                 Roo.log("need to add dialog support");
4058                 /*
4059                 Roo.MessageBox.alert("Error",
4060                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4061                         action.result.errorMsg :
4062                         "Saving Failed, please check your entries or try again"
4063                 );
4064                 */
4065             }
4066             
4067             this.fireEvent('actionfailed', this, action);
4068         }
4069         
4070     },
4071     /**
4072      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4073      * @param {String} id The value to search for
4074      * @return Field
4075      */
4076     findField : function(id){
4077         var items = this.getItems();
4078         var field = items.get(id);
4079         if(!field){
4080              items.each(function(f){
4081                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4082                     field = f;
4083                     return false;
4084                 }
4085                 return true;
4086             });
4087         }
4088         return field || null;
4089     },
4090      /**
4091      * Mark fields in this form invalid in bulk.
4092      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4093      * @return {BasicForm} this
4094      */
4095     markInvalid : function(errors){
4096         if(errors instanceof Array){
4097             for(var i = 0, len = errors.length; i < len; i++){
4098                 var fieldError = errors[i];
4099                 var f = this.findField(fieldError.id);
4100                 if(f){
4101                     f.markInvalid(fieldError.msg);
4102                 }
4103             }
4104         }else{
4105             var field, id;
4106             for(id in errors){
4107                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4108                     field.markInvalid(errors[id]);
4109                 }
4110             }
4111         }
4112         //Roo.each(this.childForms || [], function (f) {
4113         //    f.markInvalid(errors);
4114         //});
4115         
4116         return this;
4117     },
4118
4119     /**
4120      * Set values for fields in this form in bulk.
4121      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4122      * @return {BasicForm} this
4123      */
4124     setValues : function(values){
4125         if(values instanceof Array){ // array of objects
4126             for(var i = 0, len = values.length; i < len; i++){
4127                 var v = values[i];
4128                 var f = this.findField(v.id);
4129                 if(f){
4130                     f.setValue(v.value);
4131                     if(this.trackResetOnLoad){
4132                         f.originalValue = f.getValue();
4133                     }
4134                 }
4135             }
4136         }else{ // object hash
4137             var field, id;
4138             for(id in values){
4139                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4140                     
4141                     if (field.setFromData && 
4142                         field.valueField && 
4143                         field.displayField &&
4144                         // combos' with local stores can 
4145                         // be queried via setValue()
4146                         // to set their value..
4147                         (field.store && !field.store.isLocal)
4148                         ) {
4149                         // it's a combo
4150                         var sd = { };
4151                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4152                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4153                         field.setFromData(sd);
4154                         
4155                     } else {
4156                         field.setValue(values[id]);
4157                     }
4158                     
4159                     
4160                     if(this.trackResetOnLoad){
4161                         field.originalValue = field.getValue();
4162                     }
4163                 }
4164             }
4165         }
4166          
4167         //Roo.each(this.childForms || [], function (f) {
4168         //    f.setValues(values);
4169         //});
4170                 
4171         return this;
4172     },
4173
4174     /**
4175      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4176      * they are returned as an array.
4177      * @param {Boolean} asString
4178      * @return {Object}
4179      */
4180     getValues : function(asString){
4181         //if (this.childForms) {
4182             // copy values from the child forms
4183         //    Roo.each(this.childForms, function (f) {
4184         //        this.setValues(f.getValues());
4185         //    }, this);
4186         //}
4187         
4188         
4189         
4190         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4191         if(asString === true){
4192             return fs;
4193         }
4194         return Roo.urlDecode(fs);
4195     },
4196     
4197     /**
4198      * Returns the fields in this form as an object with key/value pairs. 
4199      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4200      * @return {Object}
4201      */
4202     getFieldValues : function(with_hidden)
4203     {
4204         var items = this.getItems();
4205         var ret = {};
4206         items.each(function(f){
4207             if (!f.getName()) {
4208                 return;
4209             }
4210             var v = f.getValue();
4211             if (f.inputType =='radio') {
4212                 if (typeof(ret[f.getName()]) == 'undefined') {
4213                     ret[f.getName()] = ''; // empty..
4214                 }
4215                 
4216                 if (!f.el.dom.checked) {
4217                     return;
4218                     
4219                 }
4220                 v = f.el.dom.value;
4221                 
4222             }
4223             
4224             // not sure if this supported any more..
4225             if ((typeof(v) == 'object') && f.getRawValue) {
4226                 v = f.getRawValue() ; // dates..
4227             }
4228             // combo boxes where name != hiddenName...
4229             if (f.name != f.getName()) {
4230                 ret[f.name] = f.getRawValue();
4231             }
4232             ret[f.getName()] = v;
4233         });
4234         
4235         return ret;
4236     },
4237
4238     /**
4239      * Clears all invalid messages in this form.
4240      * @return {BasicForm} this
4241      */
4242     clearInvalid : function(){
4243         var items = this.getItems();
4244         
4245         items.each(function(f){
4246            f.clearInvalid();
4247         });
4248         
4249         
4250         
4251         return this;
4252     },
4253
4254     /**
4255      * Resets this form.
4256      * @return {BasicForm} this
4257      */
4258     reset : function(){
4259         var items = this.getItems();
4260         items.each(function(f){
4261             f.reset();
4262         });
4263         
4264         Roo.each(this.childForms || [], function (f) {
4265             f.reset();
4266         });
4267        
4268         
4269         return this;
4270     },
4271     getItems : function()
4272     {
4273         var r=new Roo.util.MixedCollection(false, function(o){
4274             return o.id || (o.id = Roo.id());
4275         });
4276         var iter = function(el) {
4277             if (el.inputEl) {
4278                 r.add(el);
4279             }
4280             if (!el.items) {
4281                 return;
4282             }
4283             Roo.each(el.items,function(e) {
4284                 iter(e);
4285             });
4286             
4287             
4288         };
4289         iter(this);
4290         return r;
4291         
4292         
4293         
4294         
4295     }
4296     
4297 });
4298
4299  
4300 /*
4301  * Based on:
4302  * Ext JS Library 1.1.1
4303  * Copyright(c) 2006-2007, Ext JS, LLC.
4304  *
4305  * Originally Released Under LGPL - original licence link has changed is not relivant.
4306  *
4307  * Fork - LGPL
4308  * <script type="text/javascript">
4309  */
4310 /**
4311  * @class Roo.form.VTypes
4312  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4313  * @singleton
4314  */
4315 Roo.form.VTypes = function(){
4316     // closure these in so they are only created once.
4317     var alpha = /^[a-zA-Z_]+$/;
4318     var alphanum = /^[a-zA-Z0-9_]+$/;
4319     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4320     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4321
4322     // All these messages and functions are configurable
4323     return {
4324         /**
4325          * The function used to validate email addresses
4326          * @param {String} value The email address
4327          */
4328         'email' : function(v){
4329             return email.test(v);
4330         },
4331         /**
4332          * The error text to display when the email validation function returns false
4333          * @type String
4334          */
4335         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4336         /**
4337          * The keystroke filter mask to be applied on email input
4338          * @type RegExp
4339          */
4340         'emailMask' : /[a-z0-9_\.\-@]/i,
4341
4342         /**
4343          * The function used to validate URLs
4344          * @param {String} value The URL
4345          */
4346         'url' : function(v){
4347             return url.test(v);
4348         },
4349         /**
4350          * The error text to display when the url validation function returns false
4351          * @type String
4352          */
4353         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4354         
4355         /**
4356          * The function used to validate alpha values
4357          * @param {String} value The value
4358          */
4359         'alpha' : function(v){
4360             return alpha.test(v);
4361         },
4362         /**
4363          * The error text to display when the alpha validation function returns false
4364          * @type String
4365          */
4366         'alphaText' : 'This field should only contain letters and _',
4367         /**
4368          * The keystroke filter mask to be applied on alpha input
4369          * @type RegExp
4370          */
4371         'alphaMask' : /[a-z_]/i,
4372
4373         /**
4374          * The function used to validate alphanumeric values
4375          * @param {String} value The value
4376          */
4377         'alphanum' : function(v){
4378             return alphanum.test(v);
4379         },
4380         /**
4381          * The error text to display when the alphanumeric validation function returns false
4382          * @type String
4383          */
4384         'alphanumText' : 'This field should only contain letters, numbers and _',
4385         /**
4386          * The keystroke filter mask to be applied on alphanumeric input
4387          * @type RegExp
4388          */
4389         'alphanumMask' : /[a-z0-9_]/i
4390     };
4391 }();/*
4392  * - LGPL
4393  *
4394  * Input
4395  * 
4396  */
4397
4398 /**
4399  * @class Roo.bootstrap.Input
4400  * @extends Roo.bootstrap.Component
4401  * Bootstrap Input class
4402  * @cfg {Boolean} disabled is it disabled
4403  * @cfg {String} fieldLabel - the label associated
4404  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4405  * @cfg {String} name name of the input
4406  * @cfg {string} fieldLabel - the label associated
4407  * @cfg {string}  inputType - input / file submit ...
4408  * @cfg {string} placeholder - placeholder to put in text.
4409  * @cfg {string}  before - input group add on before
4410  * @cfg {string} after - input group add on after
4411  * @cfg {string} size - (lg|sm) or leave empty..
4412  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4413  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4414  * @cfg {Number} md colspan out of 12 for computer-sized screens
4415  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4416  * @cfg {string} value default value of the input
4417  * @cfg {Number} labelWidth set the width of label (0-12)
4418  * @cfg {String} labelAlign (top|left)
4419  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4420  * 
4421  * 
4422  * @constructor
4423  * Create a new Input
4424  * @param {Object} config The config object
4425  */
4426
4427 Roo.bootstrap.Input = function(config){
4428     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4429    
4430         this.addEvents({
4431             /**
4432              * @event focus
4433              * Fires when this field receives input focus.
4434              * @param {Roo.form.Field} this
4435              */
4436             focus : true,
4437             /**
4438              * @event blur
4439              * Fires when this field loses input focus.
4440              * @param {Roo.form.Field} this
4441              */
4442             blur : true,
4443             /**
4444              * @event specialkey
4445              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4446              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4447              * @param {Roo.form.Field} this
4448              * @param {Roo.EventObject} e The event object
4449              */
4450             specialkey : true,
4451             /**
4452              * @event change
4453              * Fires just before the field blurs if the field value has changed.
4454              * @param {Roo.form.Field} this
4455              * @param {Mixed} newValue The new value
4456              * @param {Mixed} oldValue The original value
4457              */
4458             change : true,
4459             /**
4460              * @event invalid
4461              * Fires after the field has been marked as invalid.
4462              * @param {Roo.form.Field} this
4463              * @param {String} msg The validation message
4464              */
4465             invalid : true,
4466             /**
4467              * @event valid
4468              * Fires after the field has been validated with no errors.
4469              * @param {Roo.form.Field} this
4470              */
4471             valid : true,
4472              /**
4473              * @event keyup
4474              * Fires after the key up
4475              * @param {Roo.form.Field} this
4476              * @param {Roo.EventObject}  e The event Object
4477              */
4478             keyup : true
4479         });
4480 };
4481
4482 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4483      /**
4484      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4485       automatic validation (defaults to "keyup").
4486      */
4487     validationEvent : "keyup",
4488      /**
4489      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4490      */
4491     validateOnBlur : true,
4492     /**
4493      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4494      */
4495     validationDelay : 250,
4496      /**
4497      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4498      */
4499     focusClass : "x-form-focus",  // not needed???
4500     
4501        
4502     /**
4503      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4504      */
4505     invalidClass : "has-error",
4506     
4507     /**
4508      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4509      */
4510     selectOnFocus : false,
4511     
4512      /**
4513      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4514      */
4515     maskRe : null,
4516        /**
4517      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4518      */
4519     vtype : null,
4520     
4521       /**
4522      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4523      */
4524     disableKeyFilter : false,
4525     
4526        /**
4527      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4528      */
4529     disabled : false,
4530      /**
4531      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4532      */
4533     allowBlank : true,
4534     /**
4535      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4536      */
4537     blankText : "This field is required",
4538     
4539      /**
4540      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4541      */
4542     minLength : 0,
4543     /**
4544      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4545      */
4546     maxLength : Number.MAX_VALUE,
4547     /**
4548      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4549      */
4550     minLengthText : "The minimum length for this field is {0}",
4551     /**
4552      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4553      */
4554     maxLengthText : "The maximum length for this field is {0}",
4555   
4556     
4557     /**
4558      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4559      * If available, this function will be called only after the basic validators all return true, and will be passed the
4560      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4561      */
4562     validator : null,
4563     /**
4564      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4565      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4566      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4567      */
4568     regex : null,
4569     /**
4570      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4571      */
4572     regexText : "",
4573     
4574     
4575     
4576     fieldLabel : '',
4577     inputType : 'text',
4578     
4579     name : false,
4580     placeholder: false,
4581     before : false,
4582     after : false,
4583     size : false,
4584     // private
4585     hasFocus : false,
4586     preventMark: false,
4587     isFormField : true,
4588     value : '',
4589     labelWidth : 2,
4590     labelAlign : false,
4591     readOnly : false,
4592     
4593     parentLabelAlign : function()
4594     {
4595         var parent = this;
4596         while (parent.parent()) {
4597             parent = parent.parent();
4598             if (typeof(parent.labelAlign) !='undefined') {
4599                 return parent.labelAlign;
4600             }
4601         }
4602         return 'left';
4603         
4604     },
4605     
4606     getAutoCreate : function(){
4607         
4608         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4609         
4610         var id = Roo.id();
4611         
4612         var cfg = {};
4613         
4614         if(this.inputType != 'hidden'){
4615             cfg.cls = 'form-group' //input-group
4616         }
4617         
4618         var input =  {
4619             tag: 'input',
4620             id : id,
4621             type : this.inputType,
4622             value : this.value,
4623             cls : 'form-control',
4624             placeholder : this.placeholder || ''
4625             
4626         };
4627         
4628         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4629             input.maxLength = this.maxLength;
4630         }
4631         
4632         if (this.disabled) {
4633             input.disabled=true;
4634         }
4635         
4636         if (this.readOnly) {
4637             input.readonly=true;
4638         }
4639         
4640         if (this.name) {
4641             input.name = this.name;
4642         }
4643         if (this.size) {
4644             input.cls += ' input-' + this.size;
4645         }
4646         var settings=this;
4647         ['xs','sm','md','lg'].map(function(size){
4648             if (settings[size]) {
4649                 cfg.cls += ' col-' + size + '-' + settings[size];
4650             }
4651         });
4652         
4653         var inputblock = input;
4654         
4655         if (this.before || this.after) {
4656             
4657             inputblock = {
4658                 cls : 'input-group',
4659                 cn :  [] 
4660             };
4661             if (this.before) {
4662                 inputblock.cn.push({
4663                     tag :'span',
4664                     cls : 'input-group-addon',
4665                     html : this.before
4666                 });
4667             }
4668             inputblock.cn.push(input);
4669             if (this.after) {
4670                 inputblock.cn.push({
4671                     tag :'span',
4672                     cls : 'input-group-addon',
4673                     html : this.after
4674                 });
4675             }
4676             
4677         };
4678         
4679         if (align ==='left' && this.fieldLabel.length) {
4680                 Roo.log("left and has label");
4681                 cfg.cn = [
4682                     
4683                     {
4684                         tag: 'label',
4685                         'for' :  id,
4686                         cls : 'control-label col-sm-' + this.labelWidth,
4687                         html : this.fieldLabel
4688                         
4689                     },
4690                     {
4691                         cls : "col-sm-" + (12 - this.labelWidth), 
4692                         cn: [
4693                             inputblock
4694                         ]
4695                     }
4696                     
4697                 ];
4698         } else if ( this.fieldLabel.length) {
4699                 Roo.log(" label");
4700                  cfg.cn = [
4701                    
4702                     {
4703                         tag: 'label',
4704                         //cls : 'input-group-addon',
4705                         html : this.fieldLabel
4706                         
4707                     },
4708                     
4709                     inputblock
4710                     
4711                 ];
4712
4713         } else {
4714             
4715                 Roo.log(" no label && no align");
4716                 cfg.cn = [
4717                     
4718                         inputblock
4719                     
4720                 ];
4721                 
4722                 
4723         };
4724         Roo.log('input-parentType: ' + this.parentType);
4725         
4726         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4727            cfg.cls += ' navbar-form';
4728            Roo.log(cfg);
4729         }
4730         
4731         return cfg;
4732         
4733     },
4734     /**
4735      * return the real input element.
4736      */
4737     inputEl: function ()
4738     {
4739         return this.el.select('input.form-control',true).first();
4740     },
4741     setDisabled : function(v)
4742     {
4743         var i  = this.inputEl().dom;
4744         if (!v) {
4745             i.removeAttribute('disabled');
4746             return;
4747             
4748         }
4749         i.setAttribute('disabled','true');
4750     },
4751     initEvents : function()
4752     {
4753         
4754         this.inputEl().on("keydown" , this.fireKey,  this);
4755         this.inputEl().on("focus", this.onFocus,  this);
4756         this.inputEl().on("blur", this.onBlur,  this);
4757         
4758         this.inputEl().relayEvent('keyup', this);
4759
4760         // reference to original value for reset
4761         this.originalValue = this.getValue();
4762         //Roo.form.TextField.superclass.initEvents.call(this);
4763         if(this.validationEvent == 'keyup'){
4764             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4765             this.inputEl().on('keyup', this.filterValidation, this);
4766         }
4767         else if(this.validationEvent !== false){
4768             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4769         }
4770         
4771         if(this.selectOnFocus){
4772             this.on("focus", this.preFocus, this);
4773             
4774         }
4775         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4776             this.inputEl().on("keypress", this.filterKeys, this);
4777         }
4778        /* if(this.grow){
4779             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4780             this.el.on("click", this.autoSize,  this);
4781         }
4782         */
4783         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4784             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4785         }
4786         
4787     },
4788     filterValidation : function(e){
4789         if(!e.isNavKeyPress()){
4790             this.validationTask.delay(this.validationDelay);
4791         }
4792     },
4793      /**
4794      * Validates the field value
4795      * @return {Boolean} True if the value is valid, else false
4796      */
4797     validate : function(){
4798         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4799         if(this.disabled || this.validateValue(this.getRawValue())){
4800             this.clearInvalid();
4801             return true;
4802         }
4803         return false;
4804     },
4805     
4806     
4807     /**
4808      * Validates a value according to the field's validation rules and marks the field as invalid
4809      * if the validation fails
4810      * @param {Mixed} value The value to validate
4811      * @return {Boolean} True if the value is valid, else false
4812      */
4813     validateValue : function(value){
4814         if(value.length < 1)  { // if it's blank
4815              if(this.allowBlank){
4816                 this.clearInvalid();
4817                 return true;
4818              }else{
4819                 this.markInvalid(this.blankText);
4820                 return false;
4821              }
4822         }
4823         if(value.length < this.minLength){
4824             this.markInvalid(String.format(this.minLengthText, this.minLength));
4825             return false;
4826         }
4827         if(value.length > this.maxLength){
4828             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4829             return false;
4830         }
4831         if(this.vtype){
4832             var vt = Roo.form.VTypes;
4833             if(!vt[this.vtype](value, this)){
4834                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4835                 return false;
4836             }
4837         }
4838         if(typeof this.validator == "function"){
4839             var msg = this.validator(value);
4840             if(msg !== true){
4841                 this.markInvalid(msg);
4842                 return false;
4843             }
4844         }
4845         if(this.regex && !this.regex.test(value)){
4846             this.markInvalid(this.regexText);
4847             return false;
4848         }
4849         return true;
4850     },
4851
4852     
4853     
4854      // private
4855     fireKey : function(e){
4856         //Roo.log('field ' + e.getKey());
4857         if(e.isNavKeyPress()){
4858             this.fireEvent("specialkey", this, e);
4859         }
4860     },
4861     focus : function (selectText){
4862         if(this.rendered){
4863             this.inputEl().focus();
4864             if(selectText === true){
4865                 this.inputEl().dom.select();
4866             }
4867         }
4868         return this;
4869     } ,
4870     
4871     onFocus : function(){
4872         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4873            // this.el.addClass(this.focusClass);
4874         }
4875         if(!this.hasFocus){
4876             this.hasFocus = true;
4877             this.startValue = this.getValue();
4878             this.fireEvent("focus", this);
4879         }
4880     },
4881     
4882     beforeBlur : Roo.emptyFn,
4883
4884     
4885     // private
4886     onBlur : function(){
4887         this.beforeBlur();
4888         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4889             //this.el.removeClass(this.focusClass);
4890         }
4891         this.hasFocus = false;
4892         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4893             this.validate();
4894         }
4895         var v = this.getValue();
4896         if(String(v) !== String(this.startValue)){
4897             this.fireEvent('change', this, v, this.startValue);
4898         }
4899         this.fireEvent("blur", this);
4900     },
4901     
4902     /**
4903      * Resets the current field value to the originally loaded value and clears any validation messages
4904      */
4905     reset : function(){
4906         this.setValue(this.originalValue);
4907         this.clearInvalid();
4908     },
4909      /**
4910      * Returns the name of the field
4911      * @return {Mixed} name The name field
4912      */
4913     getName: function(){
4914         return this.name;
4915     },
4916      /**
4917      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4918      * @return {Mixed} value The field value
4919      */
4920     getValue : function(){
4921         return this.inputEl().getValue();
4922     },
4923     /**
4924      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4925      * @return {Mixed} value The field value
4926      */
4927     getRawValue : function(){
4928         var v = this.inputEl().getValue();
4929         
4930         return v;
4931     },
4932     
4933     /**
4934      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4935      * @param {Mixed} value The value to set
4936      */
4937     setRawValue : function(v){
4938         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4939     },
4940     
4941     selectText : function(start, end){
4942         var v = this.getRawValue();
4943         if(v.length > 0){
4944             start = start === undefined ? 0 : start;
4945             end = end === undefined ? v.length : end;
4946             var d = this.inputEl().dom;
4947             if(d.setSelectionRange){
4948                 d.setSelectionRange(start, end);
4949             }else if(d.createTextRange){
4950                 var range = d.createTextRange();
4951                 range.moveStart("character", start);
4952                 range.moveEnd("character", v.length-end);
4953                 range.select();
4954             }
4955         }
4956     },
4957     
4958     /**
4959      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4960      * @param {Mixed} value The value to set
4961      */
4962     setValue : function(v){
4963         this.value = v;
4964         if(this.rendered){
4965             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4966             this.validate();
4967         }
4968     },
4969     
4970     /*
4971     processValue : function(value){
4972         if(this.stripCharsRe){
4973             var newValue = value.replace(this.stripCharsRe, '');
4974             if(newValue !== value){
4975                 this.setRawValue(newValue);
4976                 return newValue;
4977             }
4978         }
4979         return value;
4980     },
4981   */
4982     preFocus : function(){
4983         
4984         if(this.selectOnFocus){
4985             this.inputEl().dom.select();
4986         }
4987     },
4988     filterKeys : function(e){
4989         var k = e.getKey();
4990         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4991             return;
4992         }
4993         var c = e.getCharCode(), cc = String.fromCharCode(c);
4994         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4995             return;
4996         }
4997         if(!this.maskRe.test(cc)){
4998             e.stopEvent();
4999         }
5000     },
5001      /**
5002      * Clear any invalid styles/messages for this field
5003      */
5004     clearInvalid : function(){
5005         
5006         if(!this.el || this.preventMark){ // not rendered
5007             return;
5008         }
5009         this.el.removeClass(this.invalidClass);
5010         /*
5011         switch(this.msgTarget){
5012             case 'qtip':
5013                 this.el.dom.qtip = '';
5014                 break;
5015             case 'title':
5016                 this.el.dom.title = '';
5017                 break;
5018             case 'under':
5019                 if(this.errorEl){
5020                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5021                 }
5022                 break;
5023             case 'side':
5024                 if(this.errorIcon){
5025                     this.errorIcon.dom.qtip = '';
5026                     this.errorIcon.hide();
5027                     this.un('resize', this.alignErrorIcon, this);
5028                 }
5029                 break;
5030             default:
5031                 var t = Roo.getDom(this.msgTarget);
5032                 t.innerHTML = '';
5033                 t.style.display = 'none';
5034                 break;
5035         }
5036         */
5037         this.fireEvent('valid', this);
5038     },
5039      /**
5040      * Mark this field as invalid
5041      * @param {String} msg The validation message
5042      */
5043     markInvalid : function(msg){
5044         if(!this.el  || this.preventMark){ // not rendered
5045             return;
5046         }
5047         this.el.addClass(this.invalidClass);
5048         /*
5049         msg = msg || this.invalidText;
5050         switch(this.msgTarget){
5051             case 'qtip':
5052                 this.el.dom.qtip = msg;
5053                 this.el.dom.qclass = 'x-form-invalid-tip';
5054                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5055                     Roo.QuickTips.enable();
5056                 }
5057                 break;
5058             case 'title':
5059                 this.el.dom.title = msg;
5060                 break;
5061             case 'under':
5062                 if(!this.errorEl){
5063                     var elp = this.el.findParent('.x-form-element', 5, true);
5064                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5065                     this.errorEl.setWidth(elp.getWidth(true)-20);
5066                 }
5067                 this.errorEl.update(msg);
5068                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5069                 break;
5070             case 'side':
5071                 if(!this.errorIcon){
5072                     var elp = this.el.findParent('.x-form-element', 5, true);
5073                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5074                 }
5075                 this.alignErrorIcon();
5076                 this.errorIcon.dom.qtip = msg;
5077                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5078                 this.errorIcon.show();
5079                 this.on('resize', this.alignErrorIcon, this);
5080                 break;
5081             default:
5082                 var t = Roo.getDom(this.msgTarget);
5083                 t.innerHTML = msg;
5084                 t.style.display = this.msgDisplay;
5085                 break;
5086         }
5087         */
5088         this.fireEvent('invalid', this, msg);
5089     },
5090     // private
5091     SafariOnKeyDown : function(event)
5092     {
5093         // this is a workaround for a password hang bug on chrome/ webkit.
5094         
5095         var isSelectAll = false;
5096         
5097         if(this.inputEl().dom.selectionEnd > 0){
5098             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5099         }
5100         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5101             event.preventDefault();
5102             this.setValue('');
5103             return;
5104         }
5105         
5106         if(isSelectAll){ // backspace and delete key
5107             
5108             event.preventDefault();
5109             // this is very hacky as keydown always get's upper case.
5110             //
5111             var cc = String.fromCharCode(event.getCharCode());
5112             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5113             
5114         }
5115     },
5116     adjustWidth : function(tag, w){
5117         tag = tag.toLowerCase();
5118         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5119             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5120                 if(tag == 'input'){
5121                     return w + 2;
5122                 }
5123                 if(tag == 'textarea'){
5124                     return w-2;
5125                 }
5126             }else if(Roo.isOpera){
5127                 if(tag == 'input'){
5128                     return w + 2;
5129                 }
5130                 if(tag == 'textarea'){
5131                     return w-2;
5132                 }
5133             }
5134         }
5135         return w;
5136     }
5137     
5138 });
5139
5140  
5141 /*
5142  * - LGPL
5143  *
5144  * Input
5145  * 
5146  */
5147
5148 /**
5149  * @class Roo.bootstrap.TextArea
5150  * @extends Roo.bootstrap.Input
5151  * Bootstrap TextArea class
5152  * @cfg {Number} cols Specifies the visible width of a text area
5153  * @cfg {Number} rows Specifies the visible number of lines in a text area
5154  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5155  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5156  * @cfg {string} html text
5157  * 
5158  * @constructor
5159  * Create a new TextArea
5160  * @param {Object} config The config object
5161  */
5162
5163 Roo.bootstrap.TextArea = function(config){
5164     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5165    
5166 };
5167
5168 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5169      
5170     cols : false,
5171     rows : 5,
5172     readOnly : false,
5173     warp : 'soft',
5174     resize : false,
5175     value: false,
5176     html: false,
5177     
5178     getAutoCreate : function(){
5179         
5180         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5181         
5182         var id = Roo.id();
5183         
5184         var cfg = {};
5185         
5186         var input =  {
5187             tag: 'textarea',
5188             id : id,
5189             warp : this.warp,
5190             rows : this.rows,
5191             value : this.value || '',
5192             html: this.html || '',
5193             cls : 'form-control',
5194             placeholder : this.placeholder || '' 
5195             
5196         };
5197         
5198         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5199             input.maxLength = this.maxLength;
5200         }
5201         
5202         if(this.resize){
5203             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5204         }
5205         
5206         if(this.cols){
5207             input.cols = this.cols;
5208         }
5209         
5210         if (this.readOnly) {
5211             input.readonly = true;
5212         }
5213         
5214         if (this.name) {
5215             input.name = this.name;
5216         }
5217         
5218         if (this.size) {
5219             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5220         }
5221         
5222         var settings=this;
5223         ['xs','sm','md','lg'].map(function(size){
5224             if (settings[size]) {
5225                 cfg.cls += ' col-' + size + '-' + settings[size];
5226             }
5227         });
5228         
5229         var inputblock = input;
5230         
5231         if (this.before || this.after) {
5232             
5233             inputblock = {
5234                 cls : 'input-group',
5235                 cn :  [] 
5236             };
5237             if (this.before) {
5238                 inputblock.cn.push({
5239                     tag :'span',
5240                     cls : 'input-group-addon',
5241                     html : this.before
5242                 });
5243             }
5244             inputblock.cn.push(input);
5245             if (this.after) {
5246                 inputblock.cn.push({
5247                     tag :'span',
5248                     cls : 'input-group-addon',
5249                     html : this.after
5250                 });
5251             }
5252             
5253         }
5254         
5255         if (align ==='left' && this.fieldLabel.length) {
5256                 Roo.log("left and has label");
5257                 cfg.cn = [
5258                     
5259                     {
5260                         tag: 'label',
5261                         'for' :  id,
5262                         cls : 'control-label col-sm-' + this.labelWidth,
5263                         html : this.fieldLabel
5264                         
5265                     },
5266                     {
5267                         cls : "col-sm-" + (12 - this.labelWidth), 
5268                         cn: [
5269                             inputblock
5270                         ]
5271                     }
5272                     
5273                 ];
5274         } else if ( this.fieldLabel.length) {
5275                 Roo.log(" label");
5276                  cfg.cn = [
5277                    
5278                     {
5279                         tag: 'label',
5280                         //cls : 'input-group-addon',
5281                         html : this.fieldLabel
5282                         
5283                     },
5284                     
5285                     inputblock
5286                     
5287                 ];
5288
5289         } else {
5290             
5291                    Roo.log(" no label && no align");
5292                 cfg.cn = [
5293                     
5294                         inputblock
5295                     
5296                 ];
5297                 
5298                 
5299         }
5300         
5301         if (this.disabled) {
5302             input.disabled=true;
5303         }
5304         
5305         return cfg;
5306         
5307     },
5308     /**
5309      * return the real textarea element.
5310      */
5311     inputEl: function ()
5312     {
5313         return this.el.select('textarea.form-control',true).first();
5314     }
5315 });
5316
5317  
5318 /*
5319  * - LGPL
5320  *
5321  * trigger field - base class for combo..
5322  * 
5323  */
5324  
5325 /**
5326  * @class Roo.bootstrap.TriggerField
5327  * @extends Roo.bootstrap.Input
5328  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5329  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5330  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5331  * for which you can provide a custom implementation.  For example:
5332  * <pre><code>
5333 var trigger = new Roo.bootstrap.TriggerField();
5334 trigger.onTriggerClick = myTriggerFn;
5335 trigger.applyTo('my-field');
5336 </code></pre>
5337  *
5338  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5339  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5340  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5341  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5342  * @constructor
5343  * Create a new TriggerField.
5344  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5345  * to the base TextField)
5346  */
5347 Roo.bootstrap.TriggerField = function(config){
5348     this.mimicing = false;
5349     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5350 };
5351
5352 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5353     /**
5354      * @cfg {String} triggerClass A CSS class to apply to the trigger
5355      */
5356      /**
5357      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5358      */
5359     hideTrigger:false,
5360
5361     /** @cfg {Boolean} grow @hide */
5362     /** @cfg {Number} growMin @hide */
5363     /** @cfg {Number} growMax @hide */
5364
5365     /**
5366      * @hide 
5367      * @method
5368      */
5369     autoSize: Roo.emptyFn,
5370     // private
5371     monitorTab : true,
5372     // private
5373     deferHeight : true,
5374
5375     
5376     actionMode : 'wrap',
5377     
5378     
5379     
5380     getAutoCreate : function(){
5381        
5382         var parent = this.parent();
5383         
5384         var align = this.parentLabelAlign();
5385         
5386         var id = Roo.id();
5387         
5388         var cfg = {
5389             cls: 'form-group' //input-group
5390         };
5391         
5392         
5393         var input =  {
5394             tag: 'input',
5395             id : id,
5396             type : this.inputType,
5397             cls : 'form-control',
5398             autocomplete: 'off',
5399             placeholder : this.placeholder || '' 
5400             
5401         };
5402         if (this.name) {
5403             input.name = this.name;
5404         }
5405         if (this.size) {
5406             input.cls += ' input-' + this.size;
5407         }
5408         
5409         if (this.disabled) {
5410             input.disabled=true;
5411         }
5412         
5413         var inputblock = input;
5414         
5415         if (this.before || this.after) {
5416             
5417             inputblock = {
5418                 cls : 'input-group',
5419                 cn :  [] 
5420             };
5421             if (this.before) {
5422                 inputblock.cn.push({
5423                     tag :'span',
5424                     cls : 'input-group-addon',
5425                     html : this.before
5426                 });
5427             }
5428             inputblock.cn.push(input);
5429             if (this.after) {
5430                 inputblock.cn.push({
5431                     tag :'span',
5432                     cls : 'input-group-addon',
5433                     html : this.after
5434                 });
5435             }
5436             
5437         };
5438         
5439         var box = {
5440             tag: 'div',
5441             cn: [
5442                 {
5443                     tag: 'input',
5444                     type : 'hidden',
5445                     cls: 'form-hidden-field'
5446                 },
5447                 inputblock
5448             ]
5449             
5450         };
5451         
5452         if(this.multiple){
5453             Roo.log('multiple');
5454             
5455             box = {
5456                 tag: 'div',
5457                 cn: [
5458                     {
5459                         tag: 'input',
5460                         type : 'hidden',
5461                         cls: 'form-hidden-field'
5462                     },
5463                     {
5464                         tag: 'ul',
5465                         cls: 'select2-choices',
5466                         cn:[
5467                             {
5468                                 tag: 'li',
5469                                 cls: 'select2-search-field',
5470                                 cn: [
5471
5472                                     inputblock
5473                                 ]
5474                             }
5475                         ]
5476                     }
5477                 ]
5478             }
5479         };
5480         
5481         var combobox = {
5482             cls: 'select2-container input-group',
5483             cn: [
5484                 box,
5485                 {
5486                     tag: 'ul',
5487                     cls: 'typeahead typeahead-long dropdown-menu',
5488                     style: 'display:none'
5489                 }
5490             ]
5491         };
5492         
5493         if(!this.multiple){
5494             combobox.cn.push({
5495                 tag :'span',
5496                 cls : 'input-group-addon btn dropdown-toggle',
5497                 cn : [
5498                     {
5499                         tag: 'span',
5500                         cls: 'caret'
5501                     },
5502                     {
5503                         tag: 'span',
5504                         cls: 'combobox-clear',
5505                         cn  : [
5506                             {
5507                                 tag : 'i',
5508                                 cls: 'icon-remove'
5509                             }
5510                         ]
5511                     }
5512                 ]
5513
5514             })
5515         }
5516         
5517         if(this.multiple){
5518             combobox.cls += ' select2-container-multi';
5519         }
5520         
5521         if (align ==='left' && this.fieldLabel.length) {
5522             
5523                 Roo.log("left and has label");
5524                 cfg.cn = [
5525                     
5526                     {
5527                         tag: 'label',
5528                         'for' :  id,
5529                         cls : 'control-label col-sm-' + this.labelWidth,
5530                         html : this.fieldLabel
5531                         
5532                     },
5533                     {
5534                         cls : "col-sm-" + (12 - this.labelWidth), 
5535                         cn: [
5536                             combobox
5537                         ]
5538                     }
5539                     
5540                 ];
5541         } else if ( this.fieldLabel.length) {
5542                 Roo.log(" label");
5543                  cfg.cn = [
5544                    
5545                     {
5546                         tag: 'label',
5547                         //cls : 'input-group-addon',
5548                         html : this.fieldLabel
5549                         
5550                     },
5551                     
5552                     combobox
5553                     
5554                 ];
5555
5556         } else {
5557             
5558                 Roo.log(" no label && no align");
5559                 cfg = combobox
5560                      
5561                 
5562         }
5563          
5564         var settings=this;
5565         ['xs','sm','md','lg'].map(function(size){
5566             if (settings[size]) {
5567                 cfg.cls += ' col-' + size + '-' + settings[size];
5568             }
5569         });
5570         
5571         return cfg;
5572         
5573     },
5574     
5575     
5576     
5577     // private
5578     onResize : function(w, h){
5579 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5580 //        if(typeof w == 'number'){
5581 //            var x = w - this.trigger.getWidth();
5582 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5583 //            this.trigger.setStyle('left', x+'px');
5584 //        }
5585     },
5586
5587     // private
5588     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5589
5590     // private
5591     getResizeEl : function(){
5592         return this.inputEl();
5593     },
5594
5595     // private
5596     getPositionEl : function(){
5597         return this.inputEl();
5598     },
5599
5600     // private
5601     alignErrorIcon : function(){
5602         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5603     },
5604
5605     // private
5606     initEvents : function(){
5607         
5608         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5609         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5610         if(!this.multiple){
5611             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5612             if(this.hideTrigger){
5613                 this.trigger.setDisplayed(false);
5614             }
5615             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5616         }
5617         
5618         if(this.multiple){
5619             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5620         }
5621         
5622         //this.trigger.addClassOnOver('x-form-trigger-over');
5623         //this.trigger.addClassOnClick('x-form-trigger-click');
5624         
5625         //if(!this.width){
5626         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5627         //}
5628     },
5629
5630     // private
5631     initTrigger : function(){
5632        
5633     },
5634
5635     // private
5636     onDestroy : function(){
5637         if(this.trigger){
5638             this.trigger.removeAllListeners();
5639           //  this.trigger.remove();
5640         }
5641         //if(this.wrap){
5642         //    this.wrap.remove();
5643         //}
5644         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5645     },
5646
5647     // private
5648     onFocus : function(){
5649         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5650         /*
5651         if(!this.mimicing){
5652             this.wrap.addClass('x-trigger-wrap-focus');
5653             this.mimicing = true;
5654             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5655             if(this.monitorTab){
5656                 this.el.on("keydown", this.checkTab, this);
5657             }
5658         }
5659         */
5660     },
5661
5662     // private
5663     checkTab : function(e){
5664         if(e.getKey() == e.TAB){
5665             this.triggerBlur();
5666         }
5667     },
5668
5669     // private
5670     onBlur : function(){
5671         // do nothing
5672     },
5673
5674     // private
5675     mimicBlur : function(e, t){
5676         /*
5677         if(!this.wrap.contains(t) && this.validateBlur()){
5678             this.triggerBlur();
5679         }
5680         */
5681     },
5682
5683     // private
5684     triggerBlur : function(){
5685         this.mimicing = false;
5686         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5687         if(this.monitorTab){
5688             this.el.un("keydown", this.checkTab, this);
5689         }
5690         //this.wrap.removeClass('x-trigger-wrap-focus');
5691         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5692     },
5693
5694     // private
5695     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5696     validateBlur : function(e, t){
5697         return true;
5698     },
5699
5700     // private
5701     onDisable : function(){
5702         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5703         //if(this.wrap){
5704         //    this.wrap.addClass('x-item-disabled');
5705         //}
5706     },
5707
5708     // private
5709     onEnable : function(){
5710         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5711         //if(this.wrap){
5712         //    this.el.removeClass('x-item-disabled');
5713         //}
5714     },
5715
5716     // private
5717     onShow : function(){
5718         var ae = this.getActionEl();
5719         
5720         if(ae){
5721             ae.dom.style.display = '';
5722             ae.dom.style.visibility = 'visible';
5723         }
5724     },
5725
5726     // private
5727     
5728     onHide : function(){
5729         var ae = this.getActionEl();
5730         ae.dom.style.display = 'none';
5731     },
5732
5733     /**
5734      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5735      * by an implementing function.
5736      * @method
5737      * @param {EventObject} e
5738      */
5739     onTriggerClick : Roo.emptyFn
5740 });
5741  /*
5742  * Based on:
5743  * Ext JS Library 1.1.1
5744  * Copyright(c) 2006-2007, Ext JS, LLC.
5745  *
5746  * Originally Released Under LGPL - original licence link has changed is not relivant.
5747  *
5748  * Fork - LGPL
5749  * <script type="text/javascript">
5750  */
5751
5752
5753 /**
5754  * @class Roo.data.SortTypes
5755  * @singleton
5756  * Defines the default sorting (casting?) comparison functions used when sorting data.
5757  */
5758 Roo.data.SortTypes = {
5759     /**
5760      * Default sort that does nothing
5761      * @param {Mixed} s The value being converted
5762      * @return {Mixed} The comparison value
5763      */
5764     none : function(s){
5765         return s;
5766     },
5767     
5768     /**
5769      * The regular expression used to strip tags
5770      * @type {RegExp}
5771      * @property
5772      */
5773     stripTagsRE : /<\/?[^>]+>/gi,
5774     
5775     /**
5776      * Strips all HTML tags to sort on text only
5777      * @param {Mixed} s The value being converted
5778      * @return {String} The comparison value
5779      */
5780     asText : function(s){
5781         return String(s).replace(this.stripTagsRE, "");
5782     },
5783     
5784     /**
5785      * Strips all HTML tags to sort on text only - Case insensitive
5786      * @param {Mixed} s The value being converted
5787      * @return {String} The comparison value
5788      */
5789     asUCText : function(s){
5790         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5791     },
5792     
5793     /**
5794      * Case insensitive string
5795      * @param {Mixed} s The value being converted
5796      * @return {String} The comparison value
5797      */
5798     asUCString : function(s) {
5799         return String(s).toUpperCase();
5800     },
5801     
5802     /**
5803      * Date sorting
5804      * @param {Mixed} s The value being converted
5805      * @return {Number} The comparison value
5806      */
5807     asDate : function(s) {
5808         if(!s){
5809             return 0;
5810         }
5811         if(s instanceof Date){
5812             return s.getTime();
5813         }
5814         return Date.parse(String(s));
5815     },
5816     
5817     /**
5818      * Float sorting
5819      * @param {Mixed} s The value being converted
5820      * @return {Float} The comparison value
5821      */
5822     asFloat : function(s) {
5823         var val = parseFloat(String(s).replace(/,/g, ""));
5824         if(isNaN(val)) val = 0;
5825         return val;
5826     },
5827     
5828     /**
5829      * Integer sorting
5830      * @param {Mixed} s The value being converted
5831      * @return {Number} The comparison value
5832      */
5833     asInt : function(s) {
5834         var val = parseInt(String(s).replace(/,/g, ""));
5835         if(isNaN(val)) val = 0;
5836         return val;
5837     }
5838 };/*
5839  * Based on:
5840  * Ext JS Library 1.1.1
5841  * Copyright(c) 2006-2007, Ext JS, LLC.
5842  *
5843  * Originally Released Under LGPL - original licence link has changed is not relivant.
5844  *
5845  * Fork - LGPL
5846  * <script type="text/javascript">
5847  */
5848
5849 /**
5850 * @class Roo.data.Record
5851  * Instances of this class encapsulate both record <em>definition</em> information, and record
5852  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5853  * to access Records cached in an {@link Roo.data.Store} object.<br>
5854  * <p>
5855  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5856  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5857  * objects.<br>
5858  * <p>
5859  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5860  * @constructor
5861  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5862  * {@link #create}. The parameters are the same.
5863  * @param {Array} data An associative Array of data values keyed by the field name.
5864  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5865  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5866  * not specified an integer id is generated.
5867  */
5868 Roo.data.Record = function(data, id){
5869     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5870     this.data = data;
5871 };
5872
5873 /**
5874  * Generate a constructor for a specific record layout.
5875  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5876  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5877  * Each field definition object may contain the following properties: <ul>
5878  * <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,
5879  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5880  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5881  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5882  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5883  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5884  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5885  * this may be omitted.</p></li>
5886  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5887  * <ul><li>auto (Default, implies no conversion)</li>
5888  * <li>string</li>
5889  * <li>int</li>
5890  * <li>float</li>
5891  * <li>boolean</li>
5892  * <li>date</li></ul></p></li>
5893  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5894  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5895  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5896  * by the Reader into an object that will be stored in the Record. It is passed the
5897  * following parameters:<ul>
5898  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5899  * </ul></p></li>
5900  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5901  * </ul>
5902  * <br>usage:<br><pre><code>
5903 var TopicRecord = Roo.data.Record.create(
5904     {name: 'title', mapping: 'topic_title'},
5905     {name: 'author', mapping: 'username'},
5906     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5907     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5908     {name: 'lastPoster', mapping: 'user2'},
5909     {name: 'excerpt', mapping: 'post_text'}
5910 );
5911
5912 var myNewRecord = new TopicRecord({
5913     title: 'Do my job please',
5914     author: 'noobie',
5915     totalPosts: 1,
5916     lastPost: new Date(),
5917     lastPoster: 'Animal',
5918     excerpt: 'No way dude!'
5919 });
5920 myStore.add(myNewRecord);
5921 </code></pre>
5922  * @method create
5923  * @static
5924  */
5925 Roo.data.Record.create = function(o){
5926     var f = function(){
5927         f.superclass.constructor.apply(this, arguments);
5928     };
5929     Roo.extend(f, Roo.data.Record);
5930     var p = f.prototype;
5931     p.fields = new Roo.util.MixedCollection(false, function(field){
5932         return field.name;
5933     });
5934     for(var i = 0, len = o.length; i < len; i++){
5935         p.fields.add(new Roo.data.Field(o[i]));
5936     }
5937     f.getField = function(name){
5938         return p.fields.get(name);  
5939     };
5940     return f;
5941 };
5942
5943 Roo.data.Record.AUTO_ID = 1000;
5944 Roo.data.Record.EDIT = 'edit';
5945 Roo.data.Record.REJECT = 'reject';
5946 Roo.data.Record.COMMIT = 'commit';
5947
5948 Roo.data.Record.prototype = {
5949     /**
5950      * Readonly flag - true if this record has been modified.
5951      * @type Boolean
5952      */
5953     dirty : false,
5954     editing : false,
5955     error: null,
5956     modified: null,
5957
5958     // private
5959     join : function(store){
5960         this.store = store;
5961     },
5962
5963     /**
5964      * Set the named field to the specified value.
5965      * @param {String} name The name of the field to set.
5966      * @param {Object} value The value to set the field to.
5967      */
5968     set : function(name, value){
5969         if(this.data[name] == value){
5970             return;
5971         }
5972         this.dirty = true;
5973         if(!this.modified){
5974             this.modified = {};
5975         }
5976         if(typeof this.modified[name] == 'undefined'){
5977             this.modified[name] = this.data[name];
5978         }
5979         this.data[name] = value;
5980         if(!this.editing && this.store){
5981             this.store.afterEdit(this);
5982         }       
5983     },
5984
5985     /**
5986      * Get the value of the named field.
5987      * @param {String} name The name of the field to get the value of.
5988      * @return {Object} The value of the field.
5989      */
5990     get : function(name){
5991         return this.data[name]; 
5992     },
5993
5994     // private
5995     beginEdit : function(){
5996         this.editing = true;
5997         this.modified = {}; 
5998     },
5999
6000     // private
6001     cancelEdit : function(){
6002         this.editing = false;
6003         delete this.modified;
6004     },
6005
6006     // private
6007     endEdit : function(){
6008         this.editing = false;
6009         if(this.dirty && this.store){
6010             this.store.afterEdit(this);
6011         }
6012     },
6013
6014     /**
6015      * Usually called by the {@link Roo.data.Store} which owns the Record.
6016      * Rejects all changes made to the Record since either creation, or the last commit operation.
6017      * Modified fields are reverted to their original values.
6018      * <p>
6019      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6020      * of reject operations.
6021      */
6022     reject : function(){
6023         var m = this.modified;
6024         for(var n in m){
6025             if(typeof m[n] != "function"){
6026                 this.data[n] = m[n];
6027             }
6028         }
6029         this.dirty = false;
6030         delete this.modified;
6031         this.editing = false;
6032         if(this.store){
6033             this.store.afterReject(this);
6034         }
6035     },
6036
6037     /**
6038      * Usually called by the {@link Roo.data.Store} which owns the Record.
6039      * Commits all changes made to the Record since either creation, or the last commit operation.
6040      * <p>
6041      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6042      * of commit operations.
6043      */
6044     commit : function(){
6045         this.dirty = false;
6046         delete this.modified;
6047         this.editing = false;
6048         if(this.store){
6049             this.store.afterCommit(this);
6050         }
6051     },
6052
6053     // private
6054     hasError : function(){
6055         return this.error != null;
6056     },
6057
6058     // private
6059     clearError : function(){
6060         this.error = null;
6061     },
6062
6063     /**
6064      * Creates a copy of this record.
6065      * @param {String} id (optional) A new record id if you don't want to use this record's id
6066      * @return {Record}
6067      */
6068     copy : function(newId) {
6069         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6070     }
6071 };/*
6072  * Based on:
6073  * Ext JS Library 1.1.1
6074  * Copyright(c) 2006-2007, Ext JS, LLC.
6075  *
6076  * Originally Released Under LGPL - original licence link has changed is not relivant.
6077  *
6078  * Fork - LGPL
6079  * <script type="text/javascript">
6080  */
6081
6082
6083
6084 /**
6085  * @class Roo.data.Store
6086  * @extends Roo.util.Observable
6087  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6088  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6089  * <p>
6090  * 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
6091  * has no knowledge of the format of the data returned by the Proxy.<br>
6092  * <p>
6093  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6094  * instances from the data object. These records are cached and made available through accessor functions.
6095  * @constructor
6096  * Creates a new Store.
6097  * @param {Object} config A config object containing the objects needed for the Store to access data,
6098  * and read the data into Records.
6099  */
6100 Roo.data.Store = function(config){
6101     this.data = new Roo.util.MixedCollection(false);
6102     this.data.getKey = function(o){
6103         return o.id;
6104     };
6105     this.baseParams = {};
6106     // private
6107     this.paramNames = {
6108         "start" : "start",
6109         "limit" : "limit",
6110         "sort" : "sort",
6111         "dir" : "dir",
6112         "multisort" : "_multisort"
6113     };
6114
6115     if(config && config.data){
6116         this.inlineData = config.data;
6117         delete config.data;
6118     }
6119
6120     Roo.apply(this, config);
6121     
6122     if(this.reader){ // reader passed
6123         this.reader = Roo.factory(this.reader, Roo.data);
6124         this.reader.xmodule = this.xmodule || false;
6125         if(!this.recordType){
6126             this.recordType = this.reader.recordType;
6127         }
6128         if(this.reader.onMetaChange){
6129             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6130         }
6131     }
6132
6133     if(this.recordType){
6134         this.fields = this.recordType.prototype.fields;
6135     }
6136     this.modified = [];
6137
6138     this.addEvents({
6139         /**
6140          * @event datachanged
6141          * Fires when the data cache has changed, and a widget which is using this Store
6142          * as a Record cache should refresh its view.
6143          * @param {Store} this
6144          */
6145         datachanged : true,
6146         /**
6147          * @event metachange
6148          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6149          * @param {Store} this
6150          * @param {Object} meta The JSON metadata
6151          */
6152         metachange : true,
6153         /**
6154          * @event add
6155          * Fires when Records have been added to the Store
6156          * @param {Store} this
6157          * @param {Roo.data.Record[]} records The array of Records added
6158          * @param {Number} index The index at which the record(s) were added
6159          */
6160         add : true,
6161         /**
6162          * @event remove
6163          * Fires when a Record has been removed from the Store
6164          * @param {Store} this
6165          * @param {Roo.data.Record} record The Record that was removed
6166          * @param {Number} index The index at which the record was removed
6167          */
6168         remove : true,
6169         /**
6170          * @event update
6171          * Fires when a Record has been updated
6172          * @param {Store} this
6173          * @param {Roo.data.Record} record The Record that was updated
6174          * @param {String} operation The update operation being performed.  Value may be one of:
6175          * <pre><code>
6176  Roo.data.Record.EDIT
6177  Roo.data.Record.REJECT
6178  Roo.data.Record.COMMIT
6179          * </code></pre>
6180          */
6181         update : true,
6182         /**
6183          * @event clear
6184          * Fires when the data cache has been cleared.
6185          * @param {Store} this
6186          */
6187         clear : true,
6188         /**
6189          * @event beforeload
6190          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6191          * the load action will be canceled.
6192          * @param {Store} this
6193          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6194          */
6195         beforeload : true,
6196         /**
6197          * @event beforeloadadd
6198          * Fires after a new set of Records has been loaded.
6199          * @param {Store} this
6200          * @param {Roo.data.Record[]} records The Records that were loaded
6201          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6202          */
6203         beforeloadadd : true,
6204         /**
6205          * @event load
6206          * Fires after a new set of Records has been loaded, before they are added to the store.
6207          * @param {Store} this
6208          * @param {Roo.data.Record[]} records The Records that were loaded
6209          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6210          * @params {Object} return from reader
6211          */
6212         load : true,
6213         /**
6214          * @event loadexception
6215          * Fires if an exception occurs in the Proxy during loading.
6216          * Called with the signature of the Proxy's "loadexception" event.
6217          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6218          * 
6219          * @param {Proxy} 
6220          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6221          * @param {Object} load options 
6222          * @param {Object} jsonData from your request (normally this contains the Exception)
6223          */
6224         loadexception : true
6225     });
6226     
6227     if(this.proxy){
6228         this.proxy = Roo.factory(this.proxy, Roo.data);
6229         this.proxy.xmodule = this.xmodule || false;
6230         this.relayEvents(this.proxy,  ["loadexception"]);
6231     }
6232     this.sortToggle = {};
6233     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6234
6235     Roo.data.Store.superclass.constructor.call(this);
6236
6237     if(this.inlineData){
6238         this.loadData(this.inlineData);
6239         delete this.inlineData;
6240     }
6241 };
6242
6243 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6244      /**
6245     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6246     * without a remote query - used by combo/forms at present.
6247     */
6248     
6249     /**
6250     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6251     */
6252     /**
6253     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6254     */
6255     /**
6256     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6257     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6258     */
6259     /**
6260     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6261     * on any HTTP request
6262     */
6263     /**
6264     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6265     */
6266     /**
6267     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6268     */
6269     multiSort: false,
6270     /**
6271     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6272     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6273     */
6274     remoteSort : false,
6275
6276     /**
6277     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6278      * loaded or when a record is removed. (defaults to false).
6279     */
6280     pruneModifiedRecords : false,
6281
6282     // private
6283     lastOptions : null,
6284
6285     /**
6286      * Add Records to the Store and fires the add event.
6287      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6288      */
6289     add : function(records){
6290         records = [].concat(records);
6291         for(var i = 0, len = records.length; i < len; i++){
6292             records[i].join(this);
6293         }
6294         var index = this.data.length;
6295         this.data.addAll(records);
6296         this.fireEvent("add", this, records, index);
6297     },
6298
6299     /**
6300      * Remove a Record from the Store and fires the remove event.
6301      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6302      */
6303     remove : function(record){
6304         var index = this.data.indexOf(record);
6305         this.data.removeAt(index);
6306         if(this.pruneModifiedRecords){
6307             this.modified.remove(record);
6308         }
6309         this.fireEvent("remove", this, record, index);
6310     },
6311
6312     /**
6313      * Remove all Records from the Store and fires the clear event.
6314      */
6315     removeAll : function(){
6316         this.data.clear();
6317         if(this.pruneModifiedRecords){
6318             this.modified = [];
6319         }
6320         this.fireEvent("clear", this);
6321     },
6322
6323     /**
6324      * Inserts Records to the Store at the given index and fires the add event.
6325      * @param {Number} index The start index at which to insert the passed Records.
6326      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6327      */
6328     insert : function(index, records){
6329         records = [].concat(records);
6330         for(var i = 0, len = records.length; i < len; i++){
6331             this.data.insert(index, records[i]);
6332             records[i].join(this);
6333         }
6334         this.fireEvent("add", this, records, index);
6335     },
6336
6337     /**
6338      * Get the index within the cache of the passed Record.
6339      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6340      * @return {Number} The index of the passed Record. Returns -1 if not found.
6341      */
6342     indexOf : function(record){
6343         return this.data.indexOf(record);
6344     },
6345
6346     /**
6347      * Get the index within the cache of the Record with the passed id.
6348      * @param {String} id The id of the Record to find.
6349      * @return {Number} The index of the Record. Returns -1 if not found.
6350      */
6351     indexOfId : function(id){
6352         return this.data.indexOfKey(id);
6353     },
6354
6355     /**
6356      * Get the Record with the specified id.
6357      * @param {String} id The id of the Record to find.
6358      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6359      */
6360     getById : function(id){
6361         return this.data.key(id);
6362     },
6363
6364     /**
6365      * Get the Record at the specified index.
6366      * @param {Number} index The index of the Record to find.
6367      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6368      */
6369     getAt : function(index){
6370         return this.data.itemAt(index);
6371     },
6372
6373     /**
6374      * Returns a range of Records between specified indices.
6375      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6376      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6377      * @return {Roo.data.Record[]} An array of Records
6378      */
6379     getRange : function(start, end){
6380         return this.data.getRange(start, end);
6381     },
6382
6383     // private
6384     storeOptions : function(o){
6385         o = Roo.apply({}, o);
6386         delete o.callback;
6387         delete o.scope;
6388         this.lastOptions = o;
6389     },
6390
6391     /**
6392      * Loads the Record cache from the configured Proxy using the configured Reader.
6393      * <p>
6394      * If using remote paging, then the first load call must specify the <em>start</em>
6395      * and <em>limit</em> properties in the options.params property to establish the initial
6396      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6397      * <p>
6398      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6399      * and this call will return before the new data has been loaded. Perform any post-processing
6400      * in a callback function, or in a "load" event handler.</strong>
6401      * <p>
6402      * @param {Object} options An object containing properties which control loading options:<ul>
6403      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6404      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6405      * passed the following arguments:<ul>
6406      * <li>r : Roo.data.Record[]</li>
6407      * <li>options: Options object from the load call</li>
6408      * <li>success: Boolean success indicator</li></ul></li>
6409      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6410      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6411      * </ul>
6412      */
6413     load : function(options){
6414         options = options || {};
6415         if(this.fireEvent("beforeload", this, options) !== false){
6416             this.storeOptions(options);
6417             var p = Roo.apply(options.params || {}, this.baseParams);
6418             // if meta was not loaded from remote source.. try requesting it.
6419             if (!this.reader.metaFromRemote) {
6420                 p._requestMeta = 1;
6421             }
6422             if(this.sortInfo && this.remoteSort){
6423                 var pn = this.paramNames;
6424                 p[pn["sort"]] = this.sortInfo.field;
6425                 p[pn["dir"]] = this.sortInfo.direction;
6426             }
6427             if (this.multiSort) {
6428                 var pn = this.paramNames;
6429                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6430             }
6431             
6432             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6433         }
6434     },
6435
6436     /**
6437      * Reloads the Record cache from the configured Proxy using the configured Reader and
6438      * the options from the last load operation performed.
6439      * @param {Object} options (optional) An object containing properties which may override the options
6440      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6441      * the most recently used options are reused).
6442      */
6443     reload : function(options){
6444         this.load(Roo.applyIf(options||{}, this.lastOptions));
6445     },
6446
6447     // private
6448     // Called as a callback by the Reader during a load operation.
6449     loadRecords : function(o, options, success){
6450         if(!o || success === false){
6451             if(success !== false){
6452                 this.fireEvent("load", this, [], options, o);
6453             }
6454             if(options.callback){
6455                 options.callback.call(options.scope || this, [], options, false);
6456             }
6457             return;
6458         }
6459         // if data returned failure - throw an exception.
6460         if (o.success === false) {
6461             // show a message if no listener is registered.
6462             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6463                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6464             }
6465             // loadmask wil be hooked into this..
6466             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6467             return;
6468         }
6469         var r = o.records, t = o.totalRecords || r.length;
6470         
6471         this.fireEvent("beforeloadadd", this, r, options, o);
6472         
6473         if(!options || options.add !== true){
6474             if(this.pruneModifiedRecords){
6475                 this.modified = [];
6476             }
6477             for(var i = 0, len = r.length; i < len; i++){
6478                 r[i].join(this);
6479             }
6480             if(this.snapshot){
6481                 this.data = this.snapshot;
6482                 delete this.snapshot;
6483             }
6484             this.data.clear();
6485             this.data.addAll(r);
6486             this.totalLength = t;
6487             this.applySort();
6488             this.fireEvent("datachanged", this);
6489         }else{
6490             this.totalLength = Math.max(t, this.data.length+r.length);
6491             this.add(r);
6492         }
6493         this.fireEvent("load", this, r, options, o);
6494         if(options.callback){
6495             options.callback.call(options.scope || this, r, options, true);
6496         }
6497     },
6498
6499
6500     /**
6501      * Loads data from a passed data block. A Reader which understands the format of the data
6502      * must have been configured in the constructor.
6503      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6504      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6505      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6506      */
6507     loadData : function(o, append){
6508         var r = this.reader.readRecords(o);
6509         this.loadRecords(r, {add: append}, true);
6510     },
6511
6512     /**
6513      * Gets the number of cached records.
6514      * <p>
6515      * <em>If using paging, this may not be the total size of the dataset. If the data object
6516      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6517      * the data set size</em>
6518      */
6519     getCount : function(){
6520         return this.data.length || 0;
6521     },
6522
6523     /**
6524      * Gets the total number of records in the dataset as returned by the server.
6525      * <p>
6526      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6527      * the dataset size</em>
6528      */
6529     getTotalCount : function(){
6530         return this.totalLength || 0;
6531     },
6532
6533     /**
6534      * Returns the sort state of the Store as an object with two properties:
6535      * <pre><code>
6536  field {String} The name of the field by which the Records are sorted
6537  direction {String} The sort order, "ASC" or "DESC"
6538      * </code></pre>
6539      */
6540     getSortState : function(){
6541         return this.sortInfo;
6542     },
6543
6544     // private
6545     applySort : function(){
6546         if(this.sortInfo && !this.remoteSort){
6547             var s = this.sortInfo, f = s.field;
6548             var st = this.fields.get(f).sortType;
6549             var fn = function(r1, r2){
6550                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6551                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6552             };
6553             this.data.sort(s.direction, fn);
6554             if(this.snapshot && this.snapshot != this.data){
6555                 this.snapshot.sort(s.direction, fn);
6556             }
6557         }
6558     },
6559
6560     /**
6561      * Sets the default sort column and order to be used by the next load operation.
6562      * @param {String} fieldName The name of the field to sort by.
6563      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6564      */
6565     setDefaultSort : function(field, dir){
6566         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6567     },
6568
6569     /**
6570      * Sort the Records.
6571      * If remote sorting is used, the sort is performed on the server, and the cache is
6572      * reloaded. If local sorting is used, the cache is sorted internally.
6573      * @param {String} fieldName The name of the field to sort by.
6574      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6575      */
6576     sort : function(fieldName, dir){
6577         var f = this.fields.get(fieldName);
6578         if(!dir){
6579             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6580             
6581             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6582                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6583             }else{
6584                 dir = f.sortDir;
6585             }
6586         }
6587         this.sortToggle[f.name] = dir;
6588         this.sortInfo = {field: f.name, direction: dir};
6589         if(!this.remoteSort){
6590             this.applySort();
6591             this.fireEvent("datachanged", this);
6592         }else{
6593             this.load(this.lastOptions);
6594         }
6595     },
6596
6597     /**
6598      * Calls the specified function for each of the Records in the cache.
6599      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6600      * Returning <em>false</em> aborts and exits the iteration.
6601      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6602      */
6603     each : function(fn, scope){
6604         this.data.each(fn, scope);
6605     },
6606
6607     /**
6608      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6609      * (e.g., during paging).
6610      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6611      */
6612     getModifiedRecords : function(){
6613         return this.modified;
6614     },
6615
6616     // private
6617     createFilterFn : function(property, value, anyMatch){
6618         if(!value.exec){ // not a regex
6619             value = String(value);
6620             if(value.length == 0){
6621                 return false;
6622             }
6623             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6624         }
6625         return function(r){
6626             return value.test(r.data[property]);
6627         };
6628     },
6629
6630     /**
6631      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6632      * @param {String} property A field on your records
6633      * @param {Number} start The record index to start at (defaults to 0)
6634      * @param {Number} end The last record index to include (defaults to length - 1)
6635      * @return {Number} The sum
6636      */
6637     sum : function(property, start, end){
6638         var rs = this.data.items, v = 0;
6639         start = start || 0;
6640         end = (end || end === 0) ? end : rs.length-1;
6641
6642         for(var i = start; i <= end; i++){
6643             v += (rs[i].data[property] || 0);
6644         }
6645         return v;
6646     },
6647
6648     /**
6649      * Filter the records by a specified property.
6650      * @param {String} field A field on your records
6651      * @param {String/RegExp} value Either a string that the field
6652      * should start with or a RegExp to test against the field
6653      * @param {Boolean} anyMatch True to match any part not just the beginning
6654      */
6655     filter : function(property, value, anyMatch){
6656         var fn = this.createFilterFn(property, value, anyMatch);
6657         return fn ? this.filterBy(fn) : this.clearFilter();
6658     },
6659
6660     /**
6661      * Filter by a function. The specified function will be called with each
6662      * record in this data source. If the function returns true the record is included,
6663      * otherwise it is filtered.
6664      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6665      * @param {Object} scope (optional) The scope of the function (defaults to this)
6666      */
6667     filterBy : function(fn, scope){
6668         this.snapshot = this.snapshot || this.data;
6669         this.data = this.queryBy(fn, scope||this);
6670         this.fireEvent("datachanged", this);
6671     },
6672
6673     /**
6674      * Query the records by a specified property.
6675      * @param {String} field A field on your records
6676      * @param {String/RegExp} value Either a string that the field
6677      * should start with or a RegExp to test against the field
6678      * @param {Boolean} anyMatch True to match any part not just the beginning
6679      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6680      */
6681     query : function(property, value, anyMatch){
6682         var fn = this.createFilterFn(property, value, anyMatch);
6683         return fn ? this.queryBy(fn) : this.data.clone();
6684     },
6685
6686     /**
6687      * Query by a function. The specified function will be called with each
6688      * record in this data source. If the function returns true the record is included
6689      * in the results.
6690      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6691      * @param {Object} scope (optional) The scope of the function (defaults to this)
6692       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6693      **/
6694     queryBy : function(fn, scope){
6695         var data = this.snapshot || this.data;
6696         return data.filterBy(fn, scope||this);
6697     },
6698
6699     /**
6700      * Collects unique values for a particular dataIndex from this store.
6701      * @param {String} dataIndex The property to collect
6702      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6703      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6704      * @return {Array} An array of the unique values
6705      **/
6706     collect : function(dataIndex, allowNull, bypassFilter){
6707         var d = (bypassFilter === true && this.snapshot) ?
6708                 this.snapshot.items : this.data.items;
6709         var v, sv, r = [], l = {};
6710         for(var i = 0, len = d.length; i < len; i++){
6711             v = d[i].data[dataIndex];
6712             sv = String(v);
6713             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6714                 l[sv] = true;
6715                 r[r.length] = v;
6716             }
6717         }
6718         return r;
6719     },
6720
6721     /**
6722      * Revert to a view of the Record cache with no filtering applied.
6723      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6724      */
6725     clearFilter : function(suppressEvent){
6726         if(this.snapshot && this.snapshot != this.data){
6727             this.data = this.snapshot;
6728             delete this.snapshot;
6729             if(suppressEvent !== true){
6730                 this.fireEvent("datachanged", this);
6731             }
6732         }
6733     },
6734
6735     // private
6736     afterEdit : function(record){
6737         if(this.modified.indexOf(record) == -1){
6738             this.modified.push(record);
6739         }
6740         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6741     },
6742     
6743     // private
6744     afterReject : function(record){
6745         this.modified.remove(record);
6746         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6747     },
6748
6749     // private
6750     afterCommit : function(record){
6751         this.modified.remove(record);
6752         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6753     },
6754
6755     /**
6756      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6757      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6758      */
6759     commitChanges : function(){
6760         var m = this.modified.slice(0);
6761         this.modified = [];
6762         for(var i = 0, len = m.length; i < len; i++){
6763             m[i].commit();
6764         }
6765     },
6766
6767     /**
6768      * Cancel outstanding changes on all changed records.
6769      */
6770     rejectChanges : function(){
6771         var m = this.modified.slice(0);
6772         this.modified = [];
6773         for(var i = 0, len = m.length; i < len; i++){
6774             m[i].reject();
6775         }
6776     },
6777
6778     onMetaChange : function(meta, rtype, o){
6779         this.recordType = rtype;
6780         this.fields = rtype.prototype.fields;
6781         delete this.snapshot;
6782         this.sortInfo = meta.sortInfo || this.sortInfo;
6783         this.modified = [];
6784         this.fireEvent('metachange', this, this.reader.meta);
6785     },
6786     
6787     moveIndex : function(data, type)
6788     {
6789         var index = this.indexOf(data);
6790         
6791         var newIndex = index + type;
6792         
6793         this.remove(data);
6794         
6795         this.insert(newIndex, data);
6796         
6797     }
6798 });/*
6799  * Based on:
6800  * Ext JS Library 1.1.1
6801  * Copyright(c) 2006-2007, Ext JS, LLC.
6802  *
6803  * Originally Released Under LGPL - original licence link has changed is not relivant.
6804  *
6805  * Fork - LGPL
6806  * <script type="text/javascript">
6807  */
6808
6809 /**
6810  * @class Roo.data.SimpleStore
6811  * @extends Roo.data.Store
6812  * Small helper class to make creating Stores from Array data easier.
6813  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6814  * @cfg {Array} fields An array of field definition objects, or field name strings.
6815  * @cfg {Array} data The multi-dimensional array of data
6816  * @constructor
6817  * @param {Object} config
6818  */
6819 Roo.data.SimpleStore = function(config){
6820     Roo.data.SimpleStore.superclass.constructor.call(this, {
6821         isLocal : true,
6822         reader: new Roo.data.ArrayReader({
6823                 id: config.id
6824             },
6825             Roo.data.Record.create(config.fields)
6826         ),
6827         proxy : new Roo.data.MemoryProxy(config.data)
6828     });
6829     this.load();
6830 };
6831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6832  * Based on:
6833  * Ext JS Library 1.1.1
6834  * Copyright(c) 2006-2007, Ext JS, LLC.
6835  *
6836  * Originally Released Under LGPL - original licence link has changed is not relivant.
6837  *
6838  * Fork - LGPL
6839  * <script type="text/javascript">
6840  */
6841
6842 /**
6843 /**
6844  * @extends Roo.data.Store
6845  * @class Roo.data.JsonStore
6846  * Small helper class to make creating Stores for JSON data easier. <br/>
6847 <pre><code>
6848 var store = new Roo.data.JsonStore({
6849     url: 'get-images.php',
6850     root: 'images',
6851     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6852 });
6853 </code></pre>
6854  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6855  * JsonReader and HttpProxy (unless inline data is provided).</b>
6856  * @cfg {Array} fields An array of field definition objects, or field name strings.
6857  * @constructor
6858  * @param {Object} config
6859  */
6860 Roo.data.JsonStore = function(c){
6861     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6862         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6863         reader: new Roo.data.JsonReader(c, c.fields)
6864     }));
6865 };
6866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6867  * Based on:
6868  * Ext JS Library 1.1.1
6869  * Copyright(c) 2006-2007, Ext JS, LLC.
6870  *
6871  * Originally Released Under LGPL - original licence link has changed is not relivant.
6872  *
6873  * Fork - LGPL
6874  * <script type="text/javascript">
6875  */
6876
6877  
6878 Roo.data.Field = function(config){
6879     if(typeof config == "string"){
6880         config = {name: config};
6881     }
6882     Roo.apply(this, config);
6883     
6884     if(!this.type){
6885         this.type = "auto";
6886     }
6887     
6888     var st = Roo.data.SortTypes;
6889     // named sortTypes are supported, here we look them up
6890     if(typeof this.sortType == "string"){
6891         this.sortType = st[this.sortType];
6892     }
6893     
6894     // set default sortType for strings and dates
6895     if(!this.sortType){
6896         switch(this.type){
6897             case "string":
6898                 this.sortType = st.asUCString;
6899                 break;
6900             case "date":
6901                 this.sortType = st.asDate;
6902                 break;
6903             default:
6904                 this.sortType = st.none;
6905         }
6906     }
6907
6908     // define once
6909     var stripRe = /[\$,%]/g;
6910
6911     // prebuilt conversion function for this field, instead of
6912     // switching every time we're reading a value
6913     if(!this.convert){
6914         var cv, dateFormat = this.dateFormat;
6915         switch(this.type){
6916             case "":
6917             case "auto":
6918             case undefined:
6919                 cv = function(v){ return v; };
6920                 break;
6921             case "string":
6922                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6923                 break;
6924             case "int":
6925                 cv = function(v){
6926                     return v !== undefined && v !== null && v !== '' ?
6927                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6928                     };
6929                 break;
6930             case "float":
6931                 cv = function(v){
6932                     return v !== undefined && v !== null && v !== '' ?
6933                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6934                     };
6935                 break;
6936             case "bool":
6937             case "boolean":
6938                 cv = function(v){ return v === true || v === "true" || v == 1; };
6939                 break;
6940             case "date":
6941                 cv = function(v){
6942                     if(!v){
6943                         return '';
6944                     }
6945                     if(v instanceof Date){
6946                         return v;
6947                     }
6948                     if(dateFormat){
6949                         if(dateFormat == "timestamp"){
6950                             return new Date(v*1000);
6951                         }
6952                         return Date.parseDate(v, dateFormat);
6953                     }
6954                     var parsed = Date.parse(v);
6955                     return parsed ? new Date(parsed) : null;
6956                 };
6957              break;
6958             
6959         }
6960         this.convert = cv;
6961     }
6962 };
6963
6964 Roo.data.Field.prototype = {
6965     dateFormat: null,
6966     defaultValue: "",
6967     mapping: null,
6968     sortType : null,
6969     sortDir : "ASC"
6970 };/*
6971  * Based on:
6972  * Ext JS Library 1.1.1
6973  * Copyright(c) 2006-2007, Ext JS, LLC.
6974  *
6975  * Originally Released Under LGPL - original licence link has changed is not relivant.
6976  *
6977  * Fork - LGPL
6978  * <script type="text/javascript">
6979  */
6980  
6981 // Base class for reading structured data from a data source.  This class is intended to be
6982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6983
6984 /**
6985  * @class Roo.data.DataReader
6986  * Base class for reading structured data from a data source.  This class is intended to be
6987  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6988  */
6989
6990 Roo.data.DataReader = function(meta, recordType){
6991     
6992     this.meta = meta;
6993     
6994     this.recordType = recordType instanceof Array ? 
6995         Roo.data.Record.create(recordType) : recordType;
6996 };
6997
6998 Roo.data.DataReader.prototype = {
6999      /**
7000      * Create an empty record
7001      * @param {Object} data (optional) - overlay some values
7002      * @return {Roo.data.Record} record created.
7003      */
7004     newRow :  function(d) {
7005         var da =  {};
7006         this.recordType.prototype.fields.each(function(c) {
7007             switch( c.type) {
7008                 case 'int' : da[c.name] = 0; break;
7009                 case 'date' : da[c.name] = new Date(); break;
7010                 case 'float' : da[c.name] = 0.0; break;
7011                 case 'boolean' : da[c.name] = false; break;
7012                 default : da[c.name] = ""; break;
7013             }
7014             
7015         });
7016         return new this.recordType(Roo.apply(da, d));
7017     }
7018     
7019 };/*
7020  * Based on:
7021  * Ext JS Library 1.1.1
7022  * Copyright(c) 2006-2007, Ext JS, LLC.
7023  *
7024  * Originally Released Under LGPL - original licence link has changed is not relivant.
7025  *
7026  * Fork - LGPL
7027  * <script type="text/javascript">
7028  */
7029
7030 /**
7031  * @class Roo.data.DataProxy
7032  * @extends Roo.data.Observable
7033  * This class is an abstract base class for implementations which provide retrieval of
7034  * unformatted data objects.<br>
7035  * <p>
7036  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7037  * (of the appropriate type which knows how to parse the data object) to provide a block of
7038  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7039  * <p>
7040  * Custom implementations must implement the load method as described in
7041  * {@link Roo.data.HttpProxy#load}.
7042  */
7043 Roo.data.DataProxy = function(){
7044     this.addEvents({
7045         /**
7046          * @event beforeload
7047          * Fires before a network request is made to retrieve a data object.
7048          * @param {Object} This DataProxy object.
7049          * @param {Object} params The params parameter to the load function.
7050          */
7051         beforeload : true,
7052         /**
7053          * @event load
7054          * Fires before the load method's callback is called.
7055          * @param {Object} This DataProxy object.
7056          * @param {Object} o The data object.
7057          * @param {Object} arg The callback argument object passed to the load function.
7058          */
7059         load : true,
7060         /**
7061          * @event loadexception
7062          * Fires if an Exception occurs during data retrieval.
7063          * @param {Object} This DataProxy object.
7064          * @param {Object} o The data object.
7065          * @param {Object} arg The callback argument object passed to the load function.
7066          * @param {Object} e The Exception.
7067          */
7068         loadexception : true
7069     });
7070     Roo.data.DataProxy.superclass.constructor.call(this);
7071 };
7072
7073 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7074
7075     /**
7076      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7077      */
7078 /*
7079  * Based on:
7080  * Ext JS Library 1.1.1
7081  * Copyright(c) 2006-2007, Ext JS, LLC.
7082  *
7083  * Originally Released Under LGPL - original licence link has changed is not relivant.
7084  *
7085  * Fork - LGPL
7086  * <script type="text/javascript">
7087  */
7088 /**
7089  * @class Roo.data.MemoryProxy
7090  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7091  * to the Reader when its load method is called.
7092  * @constructor
7093  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7094  */
7095 Roo.data.MemoryProxy = function(data){
7096     if (data.data) {
7097         data = data.data;
7098     }
7099     Roo.data.MemoryProxy.superclass.constructor.call(this);
7100     this.data = data;
7101 };
7102
7103 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7104     /**
7105      * Load data from the requested source (in this case an in-memory
7106      * data object passed to the constructor), read the data object into
7107      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7108      * process that block using the passed callback.
7109      * @param {Object} params This parameter is not used by the MemoryProxy class.
7110      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7111      * object into a block of Roo.data.Records.
7112      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7113      * The function must be passed <ul>
7114      * <li>The Record block object</li>
7115      * <li>The "arg" argument from the load function</li>
7116      * <li>A boolean success indicator</li>
7117      * </ul>
7118      * @param {Object} scope The scope in which to call the callback
7119      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7120      */
7121     load : function(params, reader, callback, scope, arg){
7122         params = params || {};
7123         var result;
7124         try {
7125             result = reader.readRecords(this.data);
7126         }catch(e){
7127             this.fireEvent("loadexception", this, arg, null, e);
7128             callback.call(scope, null, arg, false);
7129             return;
7130         }
7131         callback.call(scope, result, arg, true);
7132     },
7133     
7134     // private
7135     update : function(params, records){
7136         
7137     }
7138 });/*
7139  * Based on:
7140  * Ext JS Library 1.1.1
7141  * Copyright(c) 2006-2007, Ext JS, LLC.
7142  *
7143  * Originally Released Under LGPL - original licence link has changed is not relivant.
7144  *
7145  * Fork - LGPL
7146  * <script type="text/javascript">
7147  */
7148 /**
7149  * @class Roo.data.HttpProxy
7150  * @extends Roo.data.DataProxy
7151  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7152  * configured to reference a certain URL.<br><br>
7153  * <p>
7154  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7155  * from which the running page was served.<br><br>
7156  * <p>
7157  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7158  * <p>
7159  * Be aware that to enable the browser to parse an XML document, the server must set
7160  * the Content-Type header in the HTTP response to "text/xml".
7161  * @constructor
7162  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7163  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7164  * will be used to make the request.
7165  */
7166 Roo.data.HttpProxy = function(conn){
7167     Roo.data.HttpProxy.superclass.constructor.call(this);
7168     // is conn a conn config or a real conn?
7169     this.conn = conn;
7170     this.useAjax = !conn || !conn.events;
7171   
7172 };
7173
7174 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7175     // thse are take from connection...
7176     
7177     /**
7178      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7179      */
7180     /**
7181      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7182      * extra parameters to each request made by this object. (defaults to undefined)
7183      */
7184     /**
7185      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7186      *  to each request made by this object. (defaults to undefined)
7187      */
7188     /**
7189      * @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)
7190      */
7191     /**
7192      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7193      */
7194      /**
7195      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7196      * @type Boolean
7197      */
7198   
7199
7200     /**
7201      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7202      * @type Boolean
7203      */
7204     /**
7205      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7206      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7207      * a finer-grained basis than the DataProxy events.
7208      */
7209     getConnection : function(){
7210         return this.useAjax ? Roo.Ajax : this.conn;
7211     },
7212
7213     /**
7214      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7215      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7216      * process that block using the passed callback.
7217      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7218      * for the request to the remote server.
7219      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7220      * object into a block of Roo.data.Records.
7221      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7222      * The function must be passed <ul>
7223      * <li>The Record block object</li>
7224      * <li>The "arg" argument from the load function</li>
7225      * <li>A boolean success indicator</li>
7226      * </ul>
7227      * @param {Object} scope The scope in which to call the callback
7228      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7229      */
7230     load : function(params, reader, callback, scope, arg){
7231         if(this.fireEvent("beforeload", this, params) !== false){
7232             var  o = {
7233                 params : params || {},
7234                 request: {
7235                     callback : callback,
7236                     scope : scope,
7237                     arg : arg
7238                 },
7239                 reader: reader,
7240                 callback : this.loadResponse,
7241                 scope: this
7242             };
7243             if(this.useAjax){
7244                 Roo.applyIf(o, this.conn);
7245                 if(this.activeRequest){
7246                     Roo.Ajax.abort(this.activeRequest);
7247                 }
7248                 this.activeRequest = Roo.Ajax.request(o);
7249             }else{
7250                 this.conn.request(o);
7251             }
7252         }else{
7253             callback.call(scope||this, null, arg, false);
7254         }
7255     },
7256
7257     // private
7258     loadResponse : function(o, success, response){
7259         delete this.activeRequest;
7260         if(!success){
7261             this.fireEvent("loadexception", this, o, response);
7262             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7263             return;
7264         }
7265         var result;
7266         try {
7267             result = o.reader.read(response);
7268         }catch(e){
7269             this.fireEvent("loadexception", this, o, response, e);
7270             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7271             return;
7272         }
7273         
7274         this.fireEvent("load", this, o, o.request.arg);
7275         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7276     },
7277
7278     // private
7279     update : function(dataSet){
7280
7281     },
7282
7283     // private
7284     updateResponse : function(dataSet){
7285
7286     }
7287 });/*
7288  * Based on:
7289  * Ext JS Library 1.1.1
7290  * Copyright(c) 2006-2007, Ext JS, LLC.
7291  *
7292  * Originally Released Under LGPL - original licence link has changed is not relivant.
7293  *
7294  * Fork - LGPL
7295  * <script type="text/javascript">
7296  */
7297
7298 /**
7299  * @class Roo.data.ScriptTagProxy
7300  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7301  * other than the originating domain of the running page.<br><br>
7302  * <p>
7303  * <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
7304  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7305  * <p>
7306  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7307  * source code that is used as the source inside a &lt;script> tag.<br><br>
7308  * <p>
7309  * In order for the browser to process the returned data, the server must wrap the data object
7310  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7311  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7312  * depending on whether the callback name was passed:
7313  * <p>
7314  * <pre><code>
7315 boolean scriptTag = false;
7316 String cb = request.getParameter("callback");
7317 if (cb != null) {
7318     scriptTag = true;
7319     response.setContentType("text/javascript");
7320 } else {
7321     response.setContentType("application/x-json");
7322 }
7323 Writer out = response.getWriter();
7324 if (scriptTag) {
7325     out.write(cb + "(");
7326 }
7327 out.print(dataBlock.toJsonString());
7328 if (scriptTag) {
7329     out.write(");");
7330 }
7331 </pre></code>
7332  *
7333  * @constructor
7334  * @param {Object} config A configuration object.
7335  */
7336 Roo.data.ScriptTagProxy = function(config){
7337     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7338     Roo.apply(this, config);
7339     this.head = document.getElementsByTagName("head")[0];
7340 };
7341
7342 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7343
7344 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7345     /**
7346      * @cfg {String} url The URL from which to request the data object.
7347      */
7348     /**
7349      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7350      */
7351     timeout : 30000,
7352     /**
7353      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7354      * the server the name of the callback function set up by the load call to process the returned data object.
7355      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7356      * javascript output which calls this named function passing the data object as its only parameter.
7357      */
7358     callbackParam : "callback",
7359     /**
7360      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7361      * name to the request.
7362      */
7363     nocache : true,
7364
7365     /**
7366      * Load data from the configured URL, read the data object into
7367      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7368      * process that block using the passed callback.
7369      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7370      * for the request to the remote server.
7371      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7372      * object into a block of Roo.data.Records.
7373      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7374      * The function must be passed <ul>
7375      * <li>The Record block object</li>
7376      * <li>The "arg" argument from the load function</li>
7377      * <li>A boolean success indicator</li>
7378      * </ul>
7379      * @param {Object} scope The scope in which to call the callback
7380      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7381      */
7382     load : function(params, reader, callback, scope, arg){
7383         if(this.fireEvent("beforeload", this, params) !== false){
7384
7385             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7386
7387             var url = this.url;
7388             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7389             if(this.nocache){
7390                 url += "&_dc=" + (new Date().getTime());
7391             }
7392             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7393             var trans = {
7394                 id : transId,
7395                 cb : "stcCallback"+transId,
7396                 scriptId : "stcScript"+transId,
7397                 params : params,
7398                 arg : arg,
7399                 url : url,
7400                 callback : callback,
7401                 scope : scope,
7402                 reader : reader
7403             };
7404             var conn = this;
7405
7406             window[trans.cb] = function(o){
7407                 conn.handleResponse(o, trans);
7408             };
7409
7410             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7411
7412             if(this.autoAbort !== false){
7413                 this.abort();
7414             }
7415
7416             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7417
7418             var script = document.createElement("script");
7419             script.setAttribute("src", url);
7420             script.setAttribute("type", "text/javascript");
7421             script.setAttribute("id", trans.scriptId);
7422             this.head.appendChild(script);
7423
7424             this.trans = trans;
7425         }else{
7426             callback.call(scope||this, null, arg, false);
7427         }
7428     },
7429
7430     // private
7431     isLoading : function(){
7432         return this.trans ? true : false;
7433     },
7434
7435     /**
7436      * Abort the current server request.
7437      */
7438     abort : function(){
7439         if(this.isLoading()){
7440             this.destroyTrans(this.trans);
7441         }
7442     },
7443
7444     // private
7445     destroyTrans : function(trans, isLoaded){
7446         this.head.removeChild(document.getElementById(trans.scriptId));
7447         clearTimeout(trans.timeoutId);
7448         if(isLoaded){
7449             window[trans.cb] = undefined;
7450             try{
7451                 delete window[trans.cb];
7452             }catch(e){}
7453         }else{
7454             // if hasn't been loaded, wait for load to remove it to prevent script error
7455             window[trans.cb] = function(){
7456                 window[trans.cb] = undefined;
7457                 try{
7458                     delete window[trans.cb];
7459                 }catch(e){}
7460             };
7461         }
7462     },
7463
7464     // private
7465     handleResponse : function(o, trans){
7466         this.trans = false;
7467         this.destroyTrans(trans, true);
7468         var result;
7469         try {
7470             result = trans.reader.readRecords(o);
7471         }catch(e){
7472             this.fireEvent("loadexception", this, o, trans.arg, e);
7473             trans.callback.call(trans.scope||window, null, trans.arg, false);
7474             return;
7475         }
7476         this.fireEvent("load", this, o, trans.arg);
7477         trans.callback.call(trans.scope||window, result, trans.arg, true);
7478     },
7479
7480     // private
7481     handleFailure : function(trans){
7482         this.trans = false;
7483         this.destroyTrans(trans, false);
7484         this.fireEvent("loadexception", this, null, trans.arg);
7485         trans.callback.call(trans.scope||window, null, trans.arg, false);
7486     }
7487 });/*
7488  * Based on:
7489  * Ext JS Library 1.1.1
7490  * Copyright(c) 2006-2007, Ext JS, LLC.
7491  *
7492  * Originally Released Under LGPL - original licence link has changed is not relivant.
7493  *
7494  * Fork - LGPL
7495  * <script type="text/javascript">
7496  */
7497
7498 /**
7499  * @class Roo.data.JsonReader
7500  * @extends Roo.data.DataReader
7501  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7502  * based on mappings in a provided Roo.data.Record constructor.
7503  * 
7504  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7505  * in the reply previously. 
7506  * 
7507  * <p>
7508  * Example code:
7509  * <pre><code>
7510 var RecordDef = Roo.data.Record.create([
7511     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7512     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7513 ]);
7514 var myReader = new Roo.data.JsonReader({
7515     totalProperty: "results",    // The property which contains the total dataset size (optional)
7516     root: "rows",                // The property which contains an Array of row objects
7517     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7518 }, RecordDef);
7519 </code></pre>
7520  * <p>
7521  * This would consume a JSON file like this:
7522  * <pre><code>
7523 { 'results': 2, 'rows': [
7524     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7525     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7526 }
7527 </code></pre>
7528  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7529  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7530  * paged from the remote server.
7531  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7532  * @cfg {String} root name of the property which contains the Array of row objects.
7533  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7534  * @constructor
7535  * Create a new JsonReader
7536  * @param {Object} meta Metadata configuration options
7537  * @param {Object} recordType Either an Array of field definition objects,
7538  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7539  */
7540 Roo.data.JsonReader = function(meta, recordType){
7541     
7542     meta = meta || {};
7543     // set some defaults:
7544     Roo.applyIf(meta, {
7545         totalProperty: 'total',
7546         successProperty : 'success',
7547         root : 'data',
7548         id : 'id'
7549     });
7550     
7551     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7552 };
7553 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7554     
7555     /**
7556      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7557      * Used by Store query builder to append _requestMeta to params.
7558      * 
7559      */
7560     metaFromRemote : false,
7561     /**
7562      * This method is only used by a DataProxy which has retrieved data from a remote server.
7563      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7564      * @return {Object} data A data block which is used by an Roo.data.Store object as
7565      * a cache of Roo.data.Records.
7566      */
7567     read : function(response){
7568         var json = response.responseText;
7569        
7570         var o = /* eval:var:o */ eval("("+json+")");
7571         if(!o) {
7572             throw {message: "JsonReader.read: Json object not found"};
7573         }
7574         
7575         if(o.metaData){
7576             
7577             delete this.ef;
7578             this.metaFromRemote = true;
7579             this.meta = o.metaData;
7580             this.recordType = Roo.data.Record.create(o.metaData.fields);
7581             this.onMetaChange(this.meta, this.recordType, o);
7582         }
7583         return this.readRecords(o);
7584     },
7585
7586     // private function a store will implement
7587     onMetaChange : function(meta, recordType, o){
7588
7589     },
7590
7591     /**
7592          * @ignore
7593          */
7594     simpleAccess: function(obj, subsc) {
7595         return obj[subsc];
7596     },
7597
7598         /**
7599          * @ignore
7600          */
7601     getJsonAccessor: function(){
7602         var re = /[\[\.]/;
7603         return function(expr) {
7604             try {
7605                 return(re.test(expr))
7606                     ? new Function("obj", "return obj." + expr)
7607                     : function(obj){
7608                         return obj[expr];
7609                     };
7610             } catch(e){}
7611             return Roo.emptyFn;
7612         };
7613     }(),
7614
7615     /**
7616      * Create a data block containing Roo.data.Records from an XML document.
7617      * @param {Object} o An object which contains an Array of row objects in the property specified
7618      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7619      * which contains the total size of the dataset.
7620      * @return {Object} data A data block which is used by an Roo.data.Store object as
7621      * a cache of Roo.data.Records.
7622      */
7623     readRecords : function(o){
7624         /**
7625          * After any data loads, the raw JSON data is available for further custom processing.
7626          * @type Object
7627          */
7628         this.o = o;
7629         var s = this.meta, Record = this.recordType,
7630             f = Record.prototype.fields, fi = f.items, fl = f.length;
7631
7632 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7633         if (!this.ef) {
7634             if(s.totalProperty) {
7635                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7636                 }
7637                 if(s.successProperty) {
7638                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7639                 }
7640                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7641                 if (s.id) {
7642                         var g = this.getJsonAccessor(s.id);
7643                         this.getId = function(rec) {
7644                                 var r = g(rec);
7645                                 return (r === undefined || r === "") ? null : r;
7646                         };
7647                 } else {
7648                         this.getId = function(){return null;};
7649                 }
7650             this.ef = [];
7651             for(var jj = 0; jj < fl; jj++){
7652                 f = fi[jj];
7653                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7654                 this.ef[jj] = this.getJsonAccessor(map);
7655             }
7656         }
7657
7658         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7659         if(s.totalProperty){
7660             var vt = parseInt(this.getTotal(o), 10);
7661             if(!isNaN(vt)){
7662                 totalRecords = vt;
7663             }
7664         }
7665         if(s.successProperty){
7666             var vs = this.getSuccess(o);
7667             if(vs === false || vs === 'false'){
7668                 success = false;
7669             }
7670         }
7671         var records = [];
7672             for(var i = 0; i < c; i++){
7673                     var n = root[i];
7674                 var values = {};
7675                 var id = this.getId(n);
7676                 for(var j = 0; j < fl; j++){
7677                     f = fi[j];
7678                 var v = this.ef[j](n);
7679                 if (!f.convert) {
7680                     Roo.log('missing convert for ' + f.name);
7681                     Roo.log(f);
7682                     continue;
7683                 }
7684                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7685                 }
7686                 var record = new Record(values, id);
7687                 record.json = n;
7688                 records[i] = record;
7689             }
7690             return {
7691             raw : o,
7692                 success : success,
7693                 records : records,
7694                 totalRecords : totalRecords
7695             };
7696     }
7697 });/*
7698  * Based on:
7699  * Ext JS Library 1.1.1
7700  * Copyright(c) 2006-2007, Ext JS, LLC.
7701  *
7702  * Originally Released Under LGPL - original licence link has changed is not relivant.
7703  *
7704  * Fork - LGPL
7705  * <script type="text/javascript">
7706  */
7707
7708 /**
7709  * @class Roo.data.ArrayReader
7710  * @extends Roo.data.DataReader
7711  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7712  * Each element of that Array represents a row of data fields. The
7713  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7714  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7715  * <p>
7716  * Example code:.
7717  * <pre><code>
7718 var RecordDef = Roo.data.Record.create([
7719     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7720     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7721 ]);
7722 var myReader = new Roo.data.ArrayReader({
7723     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7724 }, RecordDef);
7725 </code></pre>
7726  * <p>
7727  * This would consume an Array like this:
7728  * <pre><code>
7729 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7730   </code></pre>
7731  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7732  * @constructor
7733  * Create a new JsonReader
7734  * @param {Object} meta Metadata configuration options.
7735  * @param {Object} recordType Either an Array of field definition objects
7736  * as specified to {@link Roo.data.Record#create},
7737  * or an {@link Roo.data.Record} object
7738  * created using {@link Roo.data.Record#create}.
7739  */
7740 Roo.data.ArrayReader = function(meta, recordType){
7741     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7742 };
7743
7744 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7745     /**
7746      * Create a data block containing Roo.data.Records from an XML document.
7747      * @param {Object} o An Array of row objects which represents the dataset.
7748      * @return {Object} data A data block which is used by an Roo.data.Store object as
7749      * a cache of Roo.data.Records.
7750      */
7751     readRecords : function(o){
7752         var sid = this.meta ? this.meta.id : null;
7753         var recordType = this.recordType, fields = recordType.prototype.fields;
7754         var records = [];
7755         var root = o;
7756             for(var i = 0; i < root.length; i++){
7757                     var n = root[i];
7758                 var values = {};
7759                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7760                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7761                 var f = fields.items[j];
7762                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7763                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7764                 v = f.convert(v);
7765                 values[f.name] = v;
7766             }
7767                 var record = new recordType(values, id);
7768                 record.json = n;
7769                 records[records.length] = record;
7770             }
7771             return {
7772                 records : records,
7773                 totalRecords : records.length
7774             };
7775     }
7776 });/*
7777  * - LGPL
7778  * * 
7779  */
7780
7781 /**
7782  * @class Roo.bootstrap.ComboBox
7783  * @extends Roo.bootstrap.TriggerField
7784  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7785  * @cfg {Boolean} append (true|false) default false
7786  * @constructor
7787  * Create a new ComboBox.
7788  * @param {Object} config Configuration options
7789  */
7790 Roo.bootstrap.ComboBox = function(config){
7791     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7792     this.addEvents({
7793         /**
7794          * @event expand
7795          * Fires when the dropdown list is expanded
7796              * @param {Roo.bootstrap.ComboBox} combo This combo box
7797              */
7798         'expand' : true,
7799         /**
7800          * @event collapse
7801          * Fires when the dropdown list is collapsed
7802              * @param {Roo.bootstrap.ComboBox} combo This combo box
7803              */
7804         'collapse' : true,
7805         /**
7806          * @event beforeselect
7807          * Fires before a list item is selected. Return false to cancel the selection.
7808              * @param {Roo.bootstrap.ComboBox} combo This combo box
7809              * @param {Roo.data.Record} record The data record returned from the underlying store
7810              * @param {Number} index The index of the selected item in the dropdown list
7811              */
7812         'beforeselect' : true,
7813         /**
7814          * @event select
7815          * Fires when a list item is selected
7816              * @param {Roo.bootstrap.ComboBox} combo This combo box
7817              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7818              * @param {Number} index The index of the selected item in the dropdown list
7819              */
7820         'select' : true,
7821         /**
7822          * @event beforequery
7823          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7824          * The event object passed has these properties:
7825              * @param {Roo.bootstrap.ComboBox} combo This combo box
7826              * @param {String} query The query
7827              * @param {Boolean} forceAll true to force "all" query
7828              * @param {Boolean} cancel true to cancel the query
7829              * @param {Object} e The query event object
7830              */
7831         'beforequery': true,
7832          /**
7833          * @event add
7834          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7835              * @param {Roo.bootstrap.ComboBox} combo This combo box
7836              */
7837         'add' : true,
7838         /**
7839          * @event edit
7840          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7841              * @param {Roo.bootstrap.ComboBox} combo This combo box
7842              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7843              */
7844         'edit' : true,
7845         /**
7846          * @event remove
7847          * Fires when the remove value from the combobox array
7848              * @param {Roo.bootstrap.ComboBox} combo This combo box
7849              */
7850         'remove' : true
7851         
7852     });
7853     
7854     
7855     this.selectedIndex = -1;
7856     if(this.mode == 'local'){
7857         if(config.queryDelay === undefined){
7858             this.queryDelay = 10;
7859         }
7860         if(config.minChars === undefined){
7861             this.minChars = 0;
7862         }
7863     }
7864 };
7865
7866 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7867      
7868     /**
7869      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7870      * rendering into an Roo.Editor, defaults to false)
7871      */
7872     /**
7873      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7874      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7875      */
7876     /**
7877      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7878      */
7879     /**
7880      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7881      * the dropdown list (defaults to undefined, with no header element)
7882      */
7883
7884      /**
7885      * @cfg {String/Roo.Template} tpl The template to use to render the output
7886      */
7887      
7888      /**
7889      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7890      */
7891     listWidth: undefined,
7892     /**
7893      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7894      * mode = 'remote' or 'text' if mode = 'local')
7895      */
7896     displayField: undefined,
7897     /**
7898      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7899      * mode = 'remote' or 'value' if mode = 'local'). 
7900      * Note: use of a valueField requires the user make a selection
7901      * in order for a value to be mapped.
7902      */
7903     valueField: undefined,
7904     
7905     
7906     /**
7907      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7908      * field's data value (defaults to the underlying DOM element's name)
7909      */
7910     hiddenName: undefined,
7911     /**
7912      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7913      */
7914     listClass: '',
7915     /**
7916      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7917      */
7918     selectedClass: 'active',
7919     
7920     /**
7921      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7922      */
7923     shadow:'sides',
7924     /**
7925      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7926      * anchor positions (defaults to 'tl-bl')
7927      */
7928     listAlign: 'tl-bl?',
7929     /**
7930      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7931      */
7932     maxHeight: 300,
7933     /**
7934      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7935      * query specified by the allQuery config option (defaults to 'query')
7936      */
7937     triggerAction: 'query',
7938     /**
7939      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7940      * (defaults to 4, does not apply if editable = false)
7941      */
7942     minChars : 4,
7943     /**
7944      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7945      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7946      */
7947     typeAhead: false,
7948     /**
7949      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7950      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7951      */
7952     queryDelay: 500,
7953     /**
7954      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7955      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7956      */
7957     pageSize: 0,
7958     /**
7959      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7960      * when editable = true (defaults to false)
7961      */
7962     selectOnFocus:false,
7963     /**
7964      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7965      */
7966     queryParam: 'query',
7967     /**
7968      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7969      * when mode = 'remote' (defaults to 'Loading...')
7970      */
7971     loadingText: 'Loading...',
7972     /**
7973      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7974      */
7975     resizable: false,
7976     /**
7977      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7978      */
7979     handleHeight : 8,
7980     /**
7981      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7982      * traditional select (defaults to true)
7983      */
7984     editable: true,
7985     /**
7986      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7987      */
7988     allQuery: '',
7989     /**
7990      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7991      */
7992     mode: 'remote',
7993     /**
7994      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7995      * listWidth has a higher value)
7996      */
7997     minListWidth : 70,
7998     /**
7999      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8000      * allow the user to set arbitrary text into the field (defaults to false)
8001      */
8002     forceSelection:false,
8003     /**
8004      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8005      * if typeAhead = true (defaults to 250)
8006      */
8007     typeAheadDelay : 250,
8008     /**
8009      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8010      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8011      */
8012     valueNotFoundText : undefined,
8013     /**
8014      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8015      */
8016     blockFocus : false,
8017     
8018     /**
8019      * @cfg {Boolean} disableClear Disable showing of clear button.
8020      */
8021     disableClear : false,
8022     /**
8023      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8024      */
8025     alwaysQuery : false,
8026     
8027     /**
8028      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8029      */
8030     multiple : false,
8031     
8032     //private
8033     addicon : false,
8034     editicon: false,
8035     
8036     page: 0,
8037     hasQuery: false,
8038     append: false,
8039     loadNext: false,
8040     item: [],
8041     
8042     // element that contains real text value.. (when hidden is used..)
8043      
8044     // private
8045     initEvents: function(){
8046         
8047         if (!this.store) {
8048             throw "can not find store for combo";
8049         }
8050         this.store = Roo.factory(this.store, Roo.data);
8051         
8052         
8053         
8054         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8055         
8056         
8057         if(this.hiddenName){
8058             
8059             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8060             
8061             this.hiddenField.dom.value =
8062                 this.hiddenValue !== undefined ? this.hiddenValue :
8063                 this.value !== undefined ? this.value : '';
8064
8065             // prevent input submission
8066             this.el.dom.removeAttribute('name');
8067             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8068              
8069              
8070         }
8071         //if(Roo.isGecko){
8072         //    this.el.dom.setAttribute('autocomplete', 'off');
8073         //}
8074
8075         var cls = 'x-combo-list';
8076         this.list = this.el.select('ul.dropdown-menu',true).first();
8077
8078         //this.list = new Roo.Layer({
8079         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8080         //});
8081         
8082         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8083         this.list.setWidth(lw);
8084         
8085         this.list.on('mouseover', this.onViewOver, this);
8086         this.list.on('mousemove', this.onViewMove, this);
8087         
8088         this.list.on('scroll', this.onViewScroll, this);
8089         
8090         /*
8091         this.list.swallowEvent('mousewheel');
8092         this.assetHeight = 0;
8093
8094         if(this.title){
8095             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8096             this.assetHeight += this.header.getHeight();
8097         }
8098
8099         this.innerList = this.list.createChild({cls:cls+'-inner'});
8100         this.innerList.on('mouseover', this.onViewOver, this);
8101         this.innerList.on('mousemove', this.onViewMove, this);
8102         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8103         
8104         if(this.allowBlank && !this.pageSize && !this.disableClear){
8105             this.footer = this.list.createChild({cls:cls+'-ft'});
8106             this.pageTb = new Roo.Toolbar(this.footer);
8107            
8108         }
8109         if(this.pageSize){
8110             this.footer = this.list.createChild({cls:cls+'-ft'});
8111             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8112                     {pageSize: this.pageSize});
8113             
8114         }
8115         
8116         if (this.pageTb && this.allowBlank && !this.disableClear) {
8117             var _this = this;
8118             this.pageTb.add(new Roo.Toolbar.Fill(), {
8119                 cls: 'x-btn-icon x-btn-clear',
8120                 text: '&#160;',
8121                 handler: function()
8122                 {
8123                     _this.collapse();
8124                     _this.clearValue();
8125                     _this.onSelect(false, -1);
8126                 }
8127             });
8128         }
8129         if (this.footer) {
8130             this.assetHeight += this.footer.getHeight();
8131         }
8132         */
8133             
8134         if(!this.tpl){
8135             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8136         }
8137
8138         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8139             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8140         });
8141         //this.view.wrapEl.setDisplayed(false);
8142         this.view.on('click', this.onViewClick, this);
8143         
8144         
8145         
8146         this.store.on('beforeload', this.onBeforeLoad, this);
8147         this.store.on('load', this.onLoad, this);
8148         this.store.on('loadexception', this.onLoadException, this);
8149         /*
8150         if(this.resizable){
8151             this.resizer = new Roo.Resizable(this.list,  {
8152                pinned:true, handles:'se'
8153             });
8154             this.resizer.on('resize', function(r, w, h){
8155                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8156                 this.listWidth = w;
8157                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8158                 this.restrictHeight();
8159             }, this);
8160             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8161         }
8162         */
8163         if(!this.editable){
8164             this.editable = true;
8165             this.setEditable(false);
8166         }
8167         
8168         /*
8169         
8170         if (typeof(this.events.add.listeners) != 'undefined') {
8171             
8172             this.addicon = this.wrap.createChild(
8173                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8174        
8175             this.addicon.on('click', function(e) {
8176                 this.fireEvent('add', this);
8177             }, this);
8178         }
8179         if (typeof(this.events.edit.listeners) != 'undefined') {
8180             
8181             this.editicon = this.wrap.createChild(
8182                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8183             if (this.addicon) {
8184                 this.editicon.setStyle('margin-left', '40px');
8185             }
8186             this.editicon.on('click', function(e) {
8187                 
8188                 // we fire even  if inothing is selected..
8189                 this.fireEvent('edit', this, this.lastData );
8190                 
8191             }, this);
8192         }
8193         */
8194         
8195         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8196             "up" : function(e){
8197                 this.inKeyMode = true;
8198                 this.selectPrev();
8199             },
8200
8201             "down" : function(e){
8202                 if(!this.isExpanded()){
8203                     this.onTriggerClick();
8204                 }else{
8205                     this.inKeyMode = true;
8206                     this.selectNext();
8207                 }
8208             },
8209
8210             "enter" : function(e){
8211                 this.onViewClick();
8212                 //return true;
8213             },
8214
8215             "esc" : function(e){
8216                 this.collapse();
8217             },
8218
8219             "tab" : function(e){
8220                 this.collapse();
8221                 
8222                 if(this.fireEvent("specialkey", this, e)){
8223                     this.onViewClick(false);
8224                 }
8225                 
8226                 return true;
8227             },
8228
8229             scope : this,
8230
8231             doRelay : function(foo, bar, hname){
8232                 if(hname == 'down' || this.scope.isExpanded()){
8233                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8234                 }
8235                 return true;
8236             },
8237
8238             forceKeyDown: true
8239         });
8240         
8241         
8242         this.queryDelay = Math.max(this.queryDelay || 10,
8243                 this.mode == 'local' ? 10 : 250);
8244         
8245         
8246         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8247         
8248         if(this.typeAhead){
8249             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8250         }
8251         if(this.editable !== false){
8252             this.inputEl().on("keyup", this.onKeyUp, this);
8253         }
8254         if(this.forceSelection){
8255             this.on('blur', this.doForce, this);
8256         }
8257         
8258         if(this.multiple){
8259             this.choices = this.el.select('ul.select2-choices', true).first();
8260             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8261         }
8262     },
8263
8264     onDestroy : function(){
8265         if(this.view){
8266             this.view.setStore(null);
8267             this.view.el.removeAllListeners();
8268             this.view.el.remove();
8269             this.view.purgeListeners();
8270         }
8271         if(this.list){
8272             this.list.dom.innerHTML  = '';
8273         }
8274         if(this.store){
8275             this.store.un('beforeload', this.onBeforeLoad, this);
8276             this.store.un('load', this.onLoad, this);
8277             this.store.un('loadexception', this.onLoadException, this);
8278         }
8279         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8280     },
8281
8282     // private
8283     fireKey : function(e){
8284         if(e.isNavKeyPress() && !this.list.isVisible()){
8285             this.fireEvent("specialkey", this, e);
8286         }
8287     },
8288
8289     // private
8290     onResize: function(w, h){
8291 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8292 //        
8293 //        if(typeof w != 'number'){
8294 //            // we do not handle it!?!?
8295 //            return;
8296 //        }
8297 //        var tw = this.trigger.getWidth();
8298 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8299 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8300 //        var x = w - tw;
8301 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8302 //            
8303 //        //this.trigger.setStyle('left', x+'px');
8304 //        
8305 //        if(this.list && this.listWidth === undefined){
8306 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8307 //            this.list.setWidth(lw);
8308 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8309 //        }
8310         
8311     
8312         
8313     },
8314
8315     /**
8316      * Allow or prevent the user from directly editing the field text.  If false is passed,
8317      * the user will only be able to select from the items defined in the dropdown list.  This method
8318      * is the runtime equivalent of setting the 'editable' config option at config time.
8319      * @param {Boolean} value True to allow the user to directly edit the field text
8320      */
8321     setEditable : function(value){
8322         if(value == this.editable){
8323             return;
8324         }
8325         this.editable = value;
8326         if(!value){
8327             this.inputEl().dom.setAttribute('readOnly', true);
8328             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8329             this.inputEl().addClass('x-combo-noedit');
8330         }else{
8331             this.inputEl().dom.setAttribute('readOnly', false);
8332             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8333             this.inputEl().removeClass('x-combo-noedit');
8334         }
8335     },
8336
8337     // private
8338     
8339     onBeforeLoad : function(combo,opts){
8340         if(!this.hasFocus){
8341             return;
8342         }
8343          if (!opts.add) {
8344             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8345          }
8346         this.restrictHeight();
8347         this.selectedIndex = -1;
8348     },
8349
8350     // private
8351     onLoad : function(){
8352         
8353         this.hasQuery = false;
8354         
8355         if(!this.hasFocus){
8356             return;
8357         }
8358         
8359         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8360             this.loading.hide();
8361         }
8362         
8363         if(this.store.getCount() > 0){
8364             this.expand();
8365             this.restrictHeight();
8366             if(this.lastQuery == this.allQuery){
8367                 if(this.editable){
8368                     this.inputEl().dom.select();
8369                 }
8370                 if(!this.selectByValue(this.value, true)){
8371                     this.select(0, true);
8372                 }
8373             }else{
8374                 this.selectNext();
8375                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8376                     this.taTask.delay(this.typeAheadDelay);
8377                 }
8378             }
8379         }else{
8380             this.onEmptyResults();
8381         }
8382         
8383         //this.el.focus();
8384     },
8385     // private
8386     onLoadException : function()
8387     {
8388         this.hasQuery = false;
8389         
8390         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8391             this.loading.hide();
8392         }
8393         
8394         this.collapse();
8395         Roo.log(this.store.reader.jsonData);
8396         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8397             // fixme
8398             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8399         }
8400         
8401         
8402     },
8403     // private
8404     onTypeAhead : function(){
8405         if(this.store.getCount() > 0){
8406             var r = this.store.getAt(0);
8407             var newValue = r.data[this.displayField];
8408             var len = newValue.length;
8409             var selStart = this.getRawValue().length;
8410             
8411             if(selStart != len){
8412                 this.setRawValue(newValue);
8413                 this.selectText(selStart, newValue.length);
8414             }
8415         }
8416     },
8417
8418     // private
8419     onSelect : function(record, index){
8420         
8421         if(this.fireEvent('beforeselect', this, record, index) !== false){
8422         
8423             this.setFromData(index > -1 ? record.data : false);
8424             
8425             this.collapse();
8426             this.fireEvent('select', this, record, index);
8427         }
8428     },
8429
8430     /**
8431      * Returns the currently selected field value or empty string if no value is set.
8432      * @return {String} value The selected value
8433      */
8434     getValue : function(){
8435         
8436         if(this.multiple){
8437             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8438         }
8439         
8440         if(this.valueField){
8441             return typeof this.value != 'undefined' ? this.value : '';
8442         }else{
8443             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8444         }
8445     },
8446
8447     /**
8448      * Clears any text/value currently set in the field
8449      */
8450     clearValue : function(){
8451         if(this.hiddenField){
8452             this.hiddenField.dom.value = '';
8453         }
8454         this.value = '';
8455         this.setRawValue('');
8456         this.lastSelectionText = '';
8457         
8458     },
8459
8460     /**
8461      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8462      * will be displayed in the field.  If the value does not match the data value of an existing item,
8463      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8464      * Otherwise the field will be blank (although the value will still be set).
8465      * @param {String} value The value to match
8466      */
8467     setValue : function(v){
8468         if(this.multiple){
8469             this.syncValue();
8470             return;
8471         }
8472         
8473         var text = v;
8474         if(this.valueField){
8475             var r = this.findRecord(this.valueField, v);
8476             if(r){
8477                 text = r.data[this.displayField];
8478             }else if(this.valueNotFoundText !== undefined){
8479                 text = this.valueNotFoundText;
8480             }
8481         }
8482         this.lastSelectionText = text;
8483         if(this.hiddenField){
8484             this.hiddenField.dom.value = v;
8485         }
8486         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8487         this.value = v;
8488     },
8489     /**
8490      * @property {Object} the last set data for the element
8491      */
8492     
8493     lastData : false,
8494     /**
8495      * Sets the value of the field based on a object which is related to the record format for the store.
8496      * @param {Object} value the value to set as. or false on reset?
8497      */
8498     setFromData : function(o){
8499         
8500         if(this.multiple){
8501             this.addItem(o);
8502             return;
8503         }
8504             
8505         var dv = ''; // display value
8506         var vv = ''; // value value..
8507         this.lastData = o;
8508         if (this.displayField) {
8509             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8510         } else {
8511             // this is an error condition!!!
8512             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8513         }
8514         
8515         if(this.valueField){
8516             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8517         }
8518         
8519         if(this.hiddenField){
8520             this.hiddenField.dom.value = vv;
8521             
8522             this.lastSelectionText = dv;
8523             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8524             this.value = vv;
8525             return;
8526         }
8527         // no hidden field.. - we store the value in 'value', but still display
8528         // display field!!!!
8529         this.lastSelectionText = dv;
8530         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8531         this.value = vv;
8532         
8533         
8534     },
8535     // private
8536     reset : function(){
8537         // overridden so that last data is reset..
8538         this.setValue(this.originalValue);
8539         this.clearInvalid();
8540         this.lastData = false;
8541         if (this.view) {
8542             this.view.clearSelections();
8543         }
8544     },
8545     // private
8546     findRecord : function(prop, value){
8547         var record;
8548         if(this.store.getCount() > 0){
8549             this.store.each(function(r){
8550                 if(r.data[prop] == value){
8551                     record = r;
8552                     return false;
8553                 }
8554                 return true;
8555             });
8556         }
8557         return record;
8558     },
8559     
8560     getName: function()
8561     {
8562         // returns hidden if it's set..
8563         if (!this.rendered) {return ''};
8564         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8565         
8566     },
8567     // private
8568     onViewMove : function(e, t){
8569         this.inKeyMode = false;
8570     },
8571
8572     // private
8573     onViewOver : function(e, t){
8574         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8575             return;
8576         }
8577         var item = this.view.findItemFromChild(t);
8578         if(item){
8579             var index = this.view.indexOf(item);
8580             this.select(index, false);
8581         }
8582     },
8583
8584     // private
8585     onViewClick : function(doFocus)
8586     {
8587         var index = this.view.getSelectedIndexes()[0];
8588         var r = this.store.getAt(index);
8589         if(r){
8590             this.onSelect(r, index);
8591         }
8592         if(doFocus !== false && !this.blockFocus){
8593             this.inputEl().focus();
8594         }
8595     },
8596
8597     // private
8598     restrictHeight : function(){
8599         //this.innerList.dom.style.height = '';
8600         //var inner = this.innerList.dom;
8601         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8602         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8603         //this.list.beginUpdate();
8604         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8605         this.list.alignTo(this.inputEl(), this.listAlign);
8606         //this.list.endUpdate();
8607     },
8608
8609     // private
8610     onEmptyResults : function(){
8611         this.collapse();
8612     },
8613
8614     /**
8615      * Returns true if the dropdown list is expanded, else false.
8616      */
8617     isExpanded : function(){
8618         return this.list.isVisible();
8619     },
8620
8621     /**
8622      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8623      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8624      * @param {String} value The data value of the item to select
8625      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8626      * selected item if it is not currently in view (defaults to true)
8627      * @return {Boolean} True if the value matched an item in the list, else false
8628      */
8629     selectByValue : function(v, scrollIntoView){
8630         if(v !== undefined && v !== null){
8631             var r = this.findRecord(this.valueField || this.displayField, v);
8632             if(r){
8633                 this.select(this.store.indexOf(r), scrollIntoView);
8634                 return true;
8635             }
8636         }
8637         return false;
8638     },
8639
8640     /**
8641      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8642      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8643      * @param {Number} index The zero-based index of the list item to select
8644      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8645      * selected item if it is not currently in view (defaults to true)
8646      */
8647     select : function(index, scrollIntoView){
8648         this.selectedIndex = index;
8649         this.view.select(index);
8650         if(scrollIntoView !== false){
8651             var el = this.view.getNode(index);
8652             if(el){
8653                 //this.innerList.scrollChildIntoView(el, false);
8654                 
8655             }
8656         }
8657     },
8658
8659     // private
8660     selectNext : function(){
8661         var ct = this.store.getCount();
8662         if(ct > 0){
8663             if(this.selectedIndex == -1){
8664                 this.select(0);
8665             }else if(this.selectedIndex < ct-1){
8666                 this.select(this.selectedIndex+1);
8667             }
8668         }
8669     },
8670
8671     // private
8672     selectPrev : function(){
8673         var ct = this.store.getCount();
8674         if(ct > 0){
8675             if(this.selectedIndex == -1){
8676                 this.select(0);
8677             }else if(this.selectedIndex != 0){
8678                 this.select(this.selectedIndex-1);
8679             }
8680         }
8681     },
8682
8683     // private
8684     onKeyUp : function(e){
8685         if(this.editable !== false && !e.isSpecialKey()){
8686             this.lastKey = e.getKey();
8687             this.dqTask.delay(this.queryDelay);
8688         }
8689     },
8690
8691     // private
8692     validateBlur : function(){
8693         return !this.list || !this.list.isVisible();   
8694     },
8695
8696     // private
8697     initQuery : function(){
8698         this.doQuery(this.getRawValue());
8699     },
8700
8701     // private
8702     doForce : function(){
8703         if(this.el.dom.value.length > 0){
8704             this.el.dom.value =
8705                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8706              
8707         }
8708     },
8709
8710     /**
8711      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8712      * query allowing the query action to be canceled if needed.
8713      * @param {String} query The SQL query to execute
8714      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8715      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8716      * saved in the current store (defaults to false)
8717      */
8718     doQuery : function(q, forceAll){
8719         
8720         if(q === undefined || q === null){
8721             q = '';
8722         }
8723         var qe = {
8724             query: q,
8725             forceAll: forceAll,
8726             combo: this,
8727             cancel:false
8728         };
8729         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8730             return false;
8731         }
8732         q = qe.query;
8733         
8734         forceAll = qe.forceAll;
8735         if(forceAll === true || (q.length >= this.minChars)){
8736             
8737             this.hasQuery = true;
8738             
8739             if(this.lastQuery != q || this.alwaysQuery){
8740                 this.lastQuery = q;
8741                 if(this.mode == 'local'){
8742                     this.selectedIndex = -1;
8743                     if(forceAll){
8744                         this.store.clearFilter();
8745                     }else{
8746                         this.store.filter(this.displayField, q);
8747                     }
8748                     this.onLoad();
8749                 }else{
8750                     this.store.baseParams[this.queryParam] = q;
8751                     
8752                     var options = {params : this.getParams(q)};
8753                     
8754                     if(this.loadNext){
8755                         options.add = true;
8756                         options.params.start = this.page * this.pageSize;
8757                     }
8758                     
8759                     this.store.load(options);
8760                     this.expand();
8761                 }
8762             }else{
8763                 this.selectedIndex = -1;
8764                 this.onLoad();   
8765             }
8766         }
8767         
8768         this.loadNext = false;
8769     },
8770
8771     // private
8772     getParams : function(q){
8773         var p = {};
8774         //p[this.queryParam] = q;
8775         
8776         if(this.pageSize){
8777             p.start = 0;
8778             p.limit = this.pageSize;
8779         }
8780         return p;
8781     },
8782
8783     /**
8784      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8785      */
8786     collapse : function(){
8787         if(!this.isExpanded()){
8788             return;
8789         }
8790         
8791         this.list.hide();
8792         Roo.get(document).un('mousedown', this.collapseIf, this);
8793         Roo.get(document).un('mousewheel', this.collapseIf, this);
8794         if (!this.editable) {
8795             Roo.get(document).un('keydown', this.listKeyPress, this);
8796         }
8797         this.fireEvent('collapse', this);
8798     },
8799
8800     // private
8801     collapseIf : function(e){
8802         var in_combo  = e.within(this.el);
8803         var in_list =  e.within(this.list);
8804         
8805         if (in_combo || in_list) {
8806             //e.stopPropagation();
8807             return;
8808         }
8809
8810         this.collapse();
8811         
8812     },
8813
8814     /**
8815      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8816      */
8817     expand : function(){
8818        
8819         if(this.isExpanded() || !this.hasFocus){
8820             return;
8821         }
8822          Roo.log('expand');
8823         this.list.alignTo(this.inputEl(), this.listAlign);
8824         this.list.show();
8825         Roo.get(document).on('mousedown', this.collapseIf, this);
8826         Roo.get(document).on('mousewheel', this.collapseIf, this);
8827         if (!this.editable) {
8828             Roo.get(document).on('keydown', this.listKeyPress, this);
8829         }
8830         
8831         this.fireEvent('expand', this);
8832     },
8833
8834     // private
8835     // Implements the default empty TriggerField.onTriggerClick function
8836     onTriggerClick : function()
8837     {
8838         Roo.log('trigger click');
8839         
8840         if(this.disabled){
8841             return;
8842         }
8843         
8844         this.page = 0;
8845         this.loadNext = false;
8846         
8847         if(this.isExpanded()){
8848             this.collapse();
8849             if (!this.blockFocus) {
8850                 this.inputEl().focus();
8851             }
8852             
8853         }else {
8854             this.hasFocus = true;
8855             if(this.triggerAction == 'all') {
8856                 this.doQuery(this.allQuery, true);
8857             } else {
8858                 this.doQuery(this.getRawValue());
8859             }
8860             if (!this.blockFocus) {
8861                 this.inputEl().focus();
8862             }
8863         }
8864     },
8865     listKeyPress : function(e)
8866     {
8867         //Roo.log('listkeypress');
8868         // scroll to first matching element based on key pres..
8869         if (e.isSpecialKey()) {
8870             return false;
8871         }
8872         var k = String.fromCharCode(e.getKey()).toUpperCase();
8873         //Roo.log(k);
8874         var match  = false;
8875         var csel = this.view.getSelectedNodes();
8876         var cselitem = false;
8877         if (csel.length) {
8878             var ix = this.view.indexOf(csel[0]);
8879             cselitem  = this.store.getAt(ix);
8880             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8881                 cselitem = false;
8882             }
8883             
8884         }
8885         
8886         this.store.each(function(v) { 
8887             if (cselitem) {
8888                 // start at existing selection.
8889                 if (cselitem.id == v.id) {
8890                     cselitem = false;
8891                 }
8892                 return true;
8893             }
8894                 
8895             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8896                 match = this.store.indexOf(v);
8897                 return false;
8898             }
8899             return true;
8900         }, this);
8901         
8902         if (match === false) {
8903             return true; // no more action?
8904         }
8905         // scroll to?
8906         this.view.select(match);
8907         var sn = Roo.get(this.view.getSelectedNodes()[0])
8908         //sn.scrollIntoView(sn.dom.parentNode, false);
8909     },
8910     
8911     onViewScroll : function(e, t){
8912         
8913         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8914             return;
8915         }
8916         
8917         this.hasQuery = true;
8918         
8919         this.loading = this.list.select('.loading', true).first();
8920         
8921         if(this.loading === null){
8922             this.list.createChild({
8923                 tag: 'div',
8924                 cls: 'loading select2-more-results select2-active',
8925                 html: 'Loading more results...'
8926             })
8927             
8928             this.loading = this.list.select('.loading', true).first();
8929             
8930             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8931             
8932             this.loading.hide();
8933         }
8934         
8935         this.loading.show();
8936         
8937         var _combo = this;
8938         
8939         this.page++;
8940         this.loadNext = true;
8941         
8942         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8943         
8944         return;
8945     },
8946     
8947     addItem : function(o)
8948     {   
8949         var dv = ''; // display value
8950         
8951         if (this.displayField) {
8952             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8953         } else {
8954             // this is an error condition!!!
8955             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8956         }
8957         
8958         if(!dv.length){
8959             return;
8960         }
8961         
8962         var choice = this.choices.createChild({
8963             tag: 'li',
8964             cls: 'select2-search-choice',
8965             cn: [
8966                 {
8967                     tag: 'div',
8968                     html: dv
8969                 },
8970                 {
8971                     tag: 'a',
8972                     href: '#',
8973                     cls: 'select2-search-choice-close',
8974                     tabindex: '-1'
8975                 }
8976             ]
8977             
8978         }, this.searchField);
8979         
8980         var close = choice.select('a.select2-search-choice-close', true).first()
8981         
8982         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8983         
8984         this.item.push(o);
8985         this.lastData = o;
8986         
8987         this.syncValue();
8988         
8989         this.inputEl().dom.value = '';
8990         
8991     },
8992     
8993     onRemoveItem : function(e, _self, o)
8994     {
8995         Roo.log('remove item');
8996         var index = this.item.indexOf(o.data) * 1;
8997         
8998         if( index < 0){
8999             Roo.log('not this item?!');
9000             return;
9001         }
9002         
9003         this.item.splice(index, 1);
9004         o.item.remove();
9005         
9006         this.syncValue();
9007         
9008         this.fireEvent('remove', this);
9009         
9010     },
9011     
9012     syncValue : function()
9013     {
9014         if(!this.item.length){
9015             this.clearValue();
9016             return;
9017         }
9018             
9019         var value = [];
9020         var _this = this;
9021         Roo.each(this.item, function(i){
9022             if(_this.valueField){
9023                 value.push(i[_this.valueField]);
9024                 return;
9025             }
9026
9027             value.push(i);
9028         });
9029
9030         this.value = value.join(',');
9031
9032         if(this.hiddenField){
9033             this.hiddenField.dom.value = this.value;
9034         }
9035     },
9036     
9037     clearItem : function()
9038     {
9039         if(!this.multiple){
9040             return;
9041         }
9042         
9043         this.item = [];
9044         
9045         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9046            c.remove();
9047         });
9048         
9049         this.syncValue();
9050     }
9051     
9052     
9053
9054     /** 
9055     * @cfg {Boolean} grow 
9056     * @hide 
9057     */
9058     /** 
9059     * @cfg {Number} growMin 
9060     * @hide 
9061     */
9062     /** 
9063     * @cfg {Number} growMax 
9064     * @hide 
9065     */
9066     /**
9067      * @hide
9068      * @method autoSize
9069      */
9070 });
9071 /*
9072  * Based on:
9073  * Ext JS Library 1.1.1
9074  * Copyright(c) 2006-2007, Ext JS, LLC.
9075  *
9076  * Originally Released Under LGPL - original licence link has changed is not relivant.
9077  *
9078  * Fork - LGPL
9079  * <script type="text/javascript">
9080  */
9081
9082 /**
9083  * @class Roo.View
9084  * @extends Roo.util.Observable
9085  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9086  * This class also supports single and multi selection modes. <br>
9087  * Create a data model bound view:
9088  <pre><code>
9089  var store = new Roo.data.Store(...);
9090
9091  var view = new Roo.View({
9092     el : "my-element",
9093     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9094  
9095     singleSelect: true,
9096     selectedClass: "ydataview-selected",
9097     store: store
9098  });
9099
9100  // listen for node click?
9101  view.on("click", function(vw, index, node, e){
9102  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9103  });
9104
9105  // load XML data
9106  dataModel.load("foobar.xml");
9107  </code></pre>
9108  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9109  * <br><br>
9110  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9111  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9112  * 
9113  * Note: old style constructor is still suported (container, template, config)
9114  * 
9115  * @constructor
9116  * Create a new View
9117  * @param {Object} config The config object
9118  * 
9119  */
9120 Roo.View = function(config, depreciated_tpl, depreciated_config){
9121     
9122     if (typeof(depreciated_tpl) == 'undefined') {
9123         // new way.. - universal constructor.
9124         Roo.apply(this, config);
9125         this.el  = Roo.get(this.el);
9126     } else {
9127         // old format..
9128         this.el  = Roo.get(config);
9129         this.tpl = depreciated_tpl;
9130         Roo.apply(this, depreciated_config);
9131     }
9132     this.wrapEl  = this.el.wrap().wrap();
9133     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9134     
9135     
9136     if(typeof(this.tpl) == "string"){
9137         this.tpl = new Roo.Template(this.tpl);
9138     } else {
9139         // support xtype ctors..
9140         this.tpl = new Roo.factory(this.tpl, Roo);
9141     }
9142     
9143     
9144     this.tpl.compile();
9145    
9146   
9147     
9148      
9149     /** @private */
9150     this.addEvents({
9151         /**
9152          * @event beforeclick
9153          * Fires before a click is processed. Returns false to cancel the default action.
9154          * @param {Roo.View} this
9155          * @param {Number} index The index of the target node
9156          * @param {HTMLElement} node The target node
9157          * @param {Roo.EventObject} e The raw event object
9158          */
9159             "beforeclick" : true,
9160         /**
9161          * @event click
9162          * Fires when a template node is clicked.
9163          * @param {Roo.View} this
9164          * @param {Number} index The index of the target node
9165          * @param {HTMLElement} node The target node
9166          * @param {Roo.EventObject} e The raw event object
9167          */
9168             "click" : true,
9169         /**
9170          * @event dblclick
9171          * Fires when a template node is double clicked.
9172          * @param {Roo.View} this
9173          * @param {Number} index The index of the target node
9174          * @param {HTMLElement} node The target node
9175          * @param {Roo.EventObject} e The raw event object
9176          */
9177             "dblclick" : true,
9178         /**
9179          * @event contextmenu
9180          * Fires when a template node is right clicked.
9181          * @param {Roo.View} this
9182          * @param {Number} index The index of the target node
9183          * @param {HTMLElement} node The target node
9184          * @param {Roo.EventObject} e The raw event object
9185          */
9186             "contextmenu" : true,
9187         /**
9188          * @event selectionchange
9189          * Fires when the selected nodes change.
9190          * @param {Roo.View} this
9191          * @param {Array} selections Array of the selected nodes
9192          */
9193             "selectionchange" : true,
9194     
9195         /**
9196          * @event beforeselect
9197          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9198          * @param {Roo.View} this
9199          * @param {HTMLElement} node The node to be selected
9200          * @param {Array} selections Array of currently selected nodes
9201          */
9202             "beforeselect" : true,
9203         /**
9204          * @event preparedata
9205          * Fires on every row to render, to allow you to change the data.
9206          * @param {Roo.View} this
9207          * @param {Object} data to be rendered (change this)
9208          */
9209           "preparedata" : true
9210           
9211           
9212         });
9213
9214
9215
9216     this.el.on({
9217         "click": this.onClick,
9218         "dblclick": this.onDblClick,
9219         "contextmenu": this.onContextMenu,
9220         scope:this
9221     });
9222
9223     this.selections = [];
9224     this.nodes = [];
9225     this.cmp = new Roo.CompositeElementLite([]);
9226     if(this.store){
9227         this.store = Roo.factory(this.store, Roo.data);
9228         this.setStore(this.store, true);
9229     }
9230     
9231     if ( this.footer && this.footer.xtype) {
9232            
9233          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9234         
9235         this.footer.dataSource = this.store
9236         this.footer.container = fctr;
9237         this.footer = Roo.factory(this.footer, Roo);
9238         fctr.insertFirst(this.el);
9239         
9240         // this is a bit insane - as the paging toolbar seems to detach the el..
9241 //        dom.parentNode.parentNode.parentNode
9242          // they get detached?
9243     }
9244     
9245     
9246     Roo.View.superclass.constructor.call(this);
9247     
9248     
9249 };
9250
9251 Roo.extend(Roo.View, Roo.util.Observable, {
9252     
9253      /**
9254      * @cfg {Roo.data.Store} store Data store to load data from.
9255      */
9256     store : false,
9257     
9258     /**
9259      * @cfg {String|Roo.Element} el The container element.
9260      */
9261     el : '',
9262     
9263     /**
9264      * @cfg {String|Roo.Template} tpl The template used by this View 
9265      */
9266     tpl : false,
9267     /**
9268      * @cfg {String} dataName the named area of the template to use as the data area
9269      *                          Works with domtemplates roo-name="name"
9270      */
9271     dataName: false,
9272     /**
9273      * @cfg {String} selectedClass The css class to add to selected nodes
9274      */
9275     selectedClass : "x-view-selected",
9276      /**
9277      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9278      */
9279     emptyText : "",
9280     
9281     /**
9282      * @cfg {String} text to display on mask (default Loading)
9283      */
9284     mask : false,
9285     /**
9286      * @cfg {Boolean} multiSelect Allow multiple selection
9287      */
9288     multiSelect : false,
9289     /**
9290      * @cfg {Boolean} singleSelect Allow single selection
9291      */
9292     singleSelect:  false,
9293     
9294     /**
9295      * @cfg {Boolean} toggleSelect - selecting 
9296      */
9297     toggleSelect : false,
9298     
9299     /**
9300      * Returns the element this view is bound to.
9301      * @return {Roo.Element}
9302      */
9303     getEl : function(){
9304         return this.wrapEl;
9305     },
9306     
9307     
9308
9309     /**
9310      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9311      */
9312     refresh : function(){
9313         Roo.log('refresh');
9314         var t = this.tpl;
9315         
9316         // if we are using something like 'domtemplate', then
9317         // the what gets used is:
9318         // t.applySubtemplate(NAME, data, wrapping data..)
9319         // the outer template then get' applied with
9320         //     the store 'extra data'
9321         // and the body get's added to the
9322         //      roo-name="data" node?
9323         //      <span class='roo-tpl-{name}'></span> ?????
9324         
9325         
9326         
9327         this.clearSelections();
9328         this.el.update("");
9329         var html = [];
9330         var records = this.store.getRange();
9331         if(records.length < 1) {
9332             
9333             // is this valid??  = should it render a template??
9334             
9335             this.el.update(this.emptyText);
9336             return;
9337         }
9338         var el = this.el;
9339         if (this.dataName) {
9340             this.el.update(t.apply(this.store.meta)); //????
9341             el = this.el.child('.roo-tpl-' + this.dataName);
9342         }
9343         
9344         for(var i = 0, len = records.length; i < len; i++){
9345             var data = this.prepareData(records[i].data, i, records[i]);
9346             this.fireEvent("preparedata", this, data, i, records[i]);
9347             html[html.length] = Roo.util.Format.trim(
9348                 this.dataName ?
9349                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9350                     t.apply(data)
9351             );
9352         }
9353         
9354         
9355         
9356         el.update(html.join(""));
9357         this.nodes = el.dom.childNodes;
9358         this.updateIndexes(0);
9359     },
9360     
9361
9362     /**
9363      * Function to override to reformat the data that is sent to
9364      * the template for each node.
9365      * DEPRICATED - use the preparedata event handler.
9366      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9367      * a JSON object for an UpdateManager bound view).
9368      */
9369     prepareData : function(data, index, record)
9370     {
9371         this.fireEvent("preparedata", this, data, index, record);
9372         return data;
9373     },
9374
9375     onUpdate : function(ds, record){
9376          Roo.log('on update');   
9377         this.clearSelections();
9378         var index = this.store.indexOf(record);
9379         var n = this.nodes[index];
9380         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9381         n.parentNode.removeChild(n);
9382         this.updateIndexes(index, index);
9383     },
9384
9385     
9386     
9387 // --------- FIXME     
9388     onAdd : function(ds, records, index)
9389     {
9390         Roo.log(['on Add', ds, records, index] );        
9391         this.clearSelections();
9392         if(this.nodes.length == 0){
9393             this.refresh();
9394             return;
9395         }
9396         var n = this.nodes[index];
9397         for(var i = 0, len = records.length; i < len; i++){
9398             var d = this.prepareData(records[i].data, i, records[i]);
9399             if(n){
9400                 this.tpl.insertBefore(n, d);
9401             }else{
9402                 
9403                 this.tpl.append(this.el, d);
9404             }
9405         }
9406         this.updateIndexes(index);
9407     },
9408
9409     onRemove : function(ds, record, index){
9410         Roo.log('onRemove');
9411         this.clearSelections();
9412         var el = this.dataName  ?
9413             this.el.child('.roo-tpl-' + this.dataName) :
9414             this.el; 
9415         
9416         el.dom.removeChild(this.nodes[index]);
9417         this.updateIndexes(index);
9418     },
9419
9420     /**
9421      * Refresh an individual node.
9422      * @param {Number} index
9423      */
9424     refreshNode : function(index){
9425         this.onUpdate(this.store, this.store.getAt(index));
9426     },
9427
9428     updateIndexes : function(startIndex, endIndex){
9429         var ns = this.nodes;
9430         startIndex = startIndex || 0;
9431         endIndex = endIndex || ns.length - 1;
9432         for(var i = startIndex; i <= endIndex; i++){
9433             ns[i].nodeIndex = i;
9434         }
9435     },
9436
9437     /**
9438      * Changes the data store this view uses and refresh the view.
9439      * @param {Store} store
9440      */
9441     setStore : function(store, initial){
9442         if(!initial && this.store){
9443             this.store.un("datachanged", this.refresh);
9444             this.store.un("add", this.onAdd);
9445             this.store.un("remove", this.onRemove);
9446             this.store.un("update", this.onUpdate);
9447             this.store.un("clear", this.refresh);
9448             this.store.un("beforeload", this.onBeforeLoad);
9449             this.store.un("load", this.onLoad);
9450             this.store.un("loadexception", this.onLoad);
9451         }
9452         if(store){
9453           
9454             store.on("datachanged", this.refresh, this);
9455             store.on("add", this.onAdd, this);
9456             store.on("remove", this.onRemove, this);
9457             store.on("update", this.onUpdate, this);
9458             store.on("clear", this.refresh, this);
9459             store.on("beforeload", this.onBeforeLoad, this);
9460             store.on("load", this.onLoad, this);
9461             store.on("loadexception", this.onLoad, this);
9462         }
9463         
9464         if(store){
9465             this.refresh();
9466         }
9467     },
9468     /**
9469      * onbeforeLoad - masks the loading area.
9470      *
9471      */
9472     onBeforeLoad : function(store,opts)
9473     {
9474          Roo.log('onBeforeLoad');   
9475         if (!opts.add) {
9476             this.el.update("");
9477         }
9478         this.el.mask(this.mask ? this.mask : "Loading" ); 
9479     },
9480     onLoad : function ()
9481     {
9482         this.el.unmask();
9483     },
9484     
9485
9486     /**
9487      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9488      * @param {HTMLElement} node
9489      * @return {HTMLElement} The template node
9490      */
9491     findItemFromChild : function(node){
9492         var el = this.dataName  ?
9493             this.el.child('.roo-tpl-' + this.dataName,true) :
9494             this.el.dom; 
9495         
9496         if(!node || node.parentNode == el){
9497                     return node;
9498             }
9499             var p = node.parentNode;
9500             while(p && p != el){
9501             if(p.parentNode == el){
9502                 return p;
9503             }
9504             p = p.parentNode;
9505         }
9506             return null;
9507     },
9508
9509     /** @ignore */
9510     onClick : function(e){
9511         var item = this.findItemFromChild(e.getTarget());
9512         if(item){
9513             var index = this.indexOf(item);
9514             if(this.onItemClick(item, index, e) !== false){
9515                 this.fireEvent("click", this, index, item, e);
9516             }
9517         }else{
9518             this.clearSelections();
9519         }
9520     },
9521
9522     /** @ignore */
9523     onContextMenu : function(e){
9524         var item = this.findItemFromChild(e.getTarget());
9525         if(item){
9526             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9527         }
9528     },
9529
9530     /** @ignore */
9531     onDblClick : function(e){
9532         var item = this.findItemFromChild(e.getTarget());
9533         if(item){
9534             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9535         }
9536     },
9537
9538     onItemClick : function(item, index, e)
9539     {
9540         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9541             return false;
9542         }
9543         if (this.toggleSelect) {
9544             var m = this.isSelected(item) ? 'unselect' : 'select';
9545             Roo.log(m);
9546             var _t = this;
9547             _t[m](item, true, false);
9548             return true;
9549         }
9550         if(this.multiSelect || this.singleSelect){
9551             if(this.multiSelect && e.shiftKey && this.lastSelection){
9552                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9553             }else{
9554                 this.select(item, this.multiSelect && e.ctrlKey);
9555                 this.lastSelection = item;
9556             }
9557             e.preventDefault();
9558         }
9559         return true;
9560     },
9561
9562     /**
9563      * Get the number of selected nodes.
9564      * @return {Number}
9565      */
9566     getSelectionCount : function(){
9567         return this.selections.length;
9568     },
9569
9570     /**
9571      * Get the currently selected nodes.
9572      * @return {Array} An array of HTMLElements
9573      */
9574     getSelectedNodes : function(){
9575         return this.selections;
9576     },
9577
9578     /**
9579      * Get the indexes of the selected nodes.
9580      * @return {Array}
9581      */
9582     getSelectedIndexes : function(){
9583         var indexes = [], s = this.selections;
9584         for(var i = 0, len = s.length; i < len; i++){
9585             indexes.push(s[i].nodeIndex);
9586         }
9587         return indexes;
9588     },
9589
9590     /**
9591      * Clear all selections
9592      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9593      */
9594     clearSelections : function(suppressEvent){
9595         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9596             this.cmp.elements = this.selections;
9597             this.cmp.removeClass(this.selectedClass);
9598             this.selections = [];
9599             if(!suppressEvent){
9600                 this.fireEvent("selectionchange", this, this.selections);
9601             }
9602         }
9603     },
9604
9605     /**
9606      * Returns true if the passed node is selected
9607      * @param {HTMLElement/Number} node The node or node index
9608      * @return {Boolean}
9609      */
9610     isSelected : function(node){
9611         var s = this.selections;
9612         if(s.length < 1){
9613             return false;
9614         }
9615         node = this.getNode(node);
9616         return s.indexOf(node) !== -1;
9617     },
9618
9619     /**
9620      * Selects nodes.
9621      * @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
9622      * @param {Boolean} keepExisting (optional) true to keep existing selections
9623      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9624      */
9625     select : function(nodeInfo, keepExisting, suppressEvent){
9626         if(nodeInfo instanceof Array){
9627             if(!keepExisting){
9628                 this.clearSelections(true);
9629             }
9630             for(var i = 0, len = nodeInfo.length; i < len; i++){
9631                 this.select(nodeInfo[i], true, true);
9632             }
9633             return;
9634         } 
9635         var node = this.getNode(nodeInfo);
9636         if(!node || this.isSelected(node)){
9637             return; // already selected.
9638         }
9639         if(!keepExisting){
9640             this.clearSelections(true);
9641         }
9642         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9643             Roo.fly(node).addClass(this.selectedClass);
9644             this.selections.push(node);
9645             if(!suppressEvent){
9646                 this.fireEvent("selectionchange", this, this.selections);
9647             }
9648         }
9649         
9650         
9651     },
9652       /**
9653      * Unselects nodes.
9654      * @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
9655      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9656      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9657      */
9658     unselect : function(nodeInfo, keepExisting, suppressEvent)
9659     {
9660         if(nodeInfo instanceof Array){
9661             Roo.each(this.selections, function(s) {
9662                 this.unselect(s, nodeInfo);
9663             }, this);
9664             return;
9665         }
9666         var node = this.getNode(nodeInfo);
9667         if(!node || !this.isSelected(node)){
9668             Roo.log("not selected");
9669             return; // not selected.
9670         }
9671         // fireevent???
9672         var ns = [];
9673         Roo.each(this.selections, function(s) {
9674             if (s == node ) {
9675                 Roo.fly(node).removeClass(this.selectedClass);
9676
9677                 return;
9678             }
9679             ns.push(s);
9680         },this);
9681         
9682         this.selections= ns;
9683         this.fireEvent("selectionchange", this, this.selections);
9684     },
9685
9686     /**
9687      * Gets a template node.
9688      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9689      * @return {HTMLElement} The node or null if it wasn't found
9690      */
9691     getNode : function(nodeInfo){
9692         if(typeof nodeInfo == "string"){
9693             return document.getElementById(nodeInfo);
9694         }else if(typeof nodeInfo == "number"){
9695             return this.nodes[nodeInfo];
9696         }
9697         return nodeInfo;
9698     },
9699
9700     /**
9701      * Gets a range template nodes.
9702      * @param {Number} startIndex
9703      * @param {Number} endIndex
9704      * @return {Array} An array of nodes
9705      */
9706     getNodes : function(start, end){
9707         var ns = this.nodes;
9708         start = start || 0;
9709         end = typeof end == "undefined" ? ns.length - 1 : end;
9710         var nodes = [];
9711         if(start <= end){
9712             for(var i = start; i <= end; i++){
9713                 nodes.push(ns[i]);
9714             }
9715         } else{
9716             for(var i = start; i >= end; i--){
9717                 nodes.push(ns[i]);
9718             }
9719         }
9720         return nodes;
9721     },
9722
9723     /**
9724      * Finds the index of the passed node
9725      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9726      * @return {Number} The index of the node or -1
9727      */
9728     indexOf : function(node){
9729         node = this.getNode(node);
9730         if(typeof node.nodeIndex == "number"){
9731             return node.nodeIndex;
9732         }
9733         var ns = this.nodes;
9734         for(var i = 0, len = ns.length; i < len; i++){
9735             if(ns[i] == node){
9736                 return i;
9737             }
9738         }
9739         return -1;
9740     }
9741 });
9742 /*
9743  * - LGPL
9744  *
9745  * based on jquery fullcalendar
9746  * 
9747  */
9748
9749 Roo.bootstrap = Roo.bootstrap || {};
9750 /**
9751  * @class Roo.bootstrap.Calendar
9752  * @extends Roo.bootstrap.Component
9753  * Bootstrap Calendar class
9754  * @cfg {Boolean} loadMask (true|false) default false
9755     
9756  * @constructor
9757  * Create a new Container
9758  * @param {Object} config The config object
9759  */
9760
9761
9762
9763 Roo.bootstrap.Calendar = function(config){
9764     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9765      this.addEvents({
9766         /**
9767              * @event select
9768              * Fires when a date is selected
9769              * @param {DatePicker} this
9770              * @param {Date} date The selected date
9771              */
9772         'select': true,
9773         /**
9774              * @event monthchange
9775              * Fires when the displayed month changes 
9776              * @param {DatePicker} this
9777              * @param {Date} date The selected month
9778              */
9779         'monthchange': true,
9780         /**
9781              * @event evententer
9782              * Fires when mouse over an event
9783              * @param {Calendar} this
9784              * @param {event} Event
9785              */
9786         'evententer': true,
9787         /**
9788              * @event eventleave
9789              * Fires when the mouse leaves an
9790              * @param {Calendar} this
9791              * @param {event}
9792              */
9793         'eventleave': true,
9794         /**
9795              * @event eventclick
9796              * Fires when the mouse click an
9797              * @param {Calendar} this
9798              * @param {event}
9799              */
9800         'eventclick': true
9801         
9802     });
9803
9804 };
9805
9806 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9807     
9808      /**
9809      * @cfg {Number} startDay
9810      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9811      */
9812     startDay : 0,
9813     
9814     loadMask : false,
9815       
9816     getAutoCreate : function(){
9817         
9818         
9819         var fc_button = function(name, corner, style, content ) {
9820             return Roo.apply({},{
9821                 tag : 'span',
9822                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9823                          (corner.length ?
9824                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9825                             ''
9826                         ),
9827                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9828                 unselectable: 'on'
9829             });
9830         };
9831         
9832         var header = {
9833             tag : 'table',
9834             cls : 'fc-header',
9835             style : 'width:100%',
9836             cn : [
9837                 {
9838                     tag: 'tr',
9839                     cn : [
9840                         {
9841                             tag : 'td',
9842                             cls : 'fc-header-left',
9843                             cn : [
9844                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9845                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9846                                 { tag: 'span', cls: 'fc-header-space' },
9847                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9848                                 
9849                                 
9850                             ]
9851                         },
9852                         
9853                         {
9854                             tag : 'td',
9855                             cls : 'fc-header-center',
9856                             cn : [
9857                                 {
9858                                     tag: 'span',
9859                                     cls: 'fc-header-title',
9860                                     cn : {
9861                                         tag: 'H2',
9862                                         html : 'month / year'
9863                                     }
9864                                 }
9865                                 
9866                             ]
9867                         },
9868                         {
9869                             tag : 'td',
9870                             cls : 'fc-header-right',
9871                             cn : [
9872                           /*      fc_button('month', 'left', '', 'month' ),
9873                                 fc_button('week', '', '', 'week' ),
9874                                 fc_button('day', 'right', '', 'day' )
9875                             */    
9876                                 
9877                             ]
9878                         }
9879                         
9880                     ]
9881                 }
9882             ]
9883         };
9884         
9885        
9886         var cal_heads = function() {
9887             var ret = [];
9888             // fixme - handle this.
9889             
9890             for (var i =0; i < Date.dayNames.length; i++) {
9891                 var d = Date.dayNames[i];
9892                 ret.push({
9893                     tag: 'th',
9894                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9895                     html : d.substring(0,3)
9896                 });
9897                 
9898             }
9899             ret[0].cls += ' fc-first';
9900             ret[6].cls += ' fc-last';
9901             return ret;
9902         };
9903         var cal_cell = function(n) {
9904             return  {
9905                 tag: 'td',
9906                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9907                 cn : [
9908                     {
9909                         cn : [
9910                             {
9911                                 cls: 'fc-day-number',
9912                                 html: 'D'
9913                             },
9914                             {
9915                                 cls: 'fc-day-content',
9916                              
9917                                 cn : [
9918                                      {
9919                                         style: 'position: relative;' // height: 17px;
9920                                     }
9921                                 ]
9922                             }
9923                             
9924                             
9925                         ]
9926                     }
9927                 ]
9928                 
9929             }
9930         };
9931         var cal_rows = function() {
9932             
9933             var ret = []
9934             for (var r = 0; r < 6; r++) {
9935                 var row= {
9936                     tag : 'tr',
9937                     cls : 'fc-week',
9938                     cn : []
9939                 };
9940                 
9941                 for (var i =0; i < Date.dayNames.length; i++) {
9942                     var d = Date.dayNames[i];
9943                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9944
9945                 }
9946                 row.cn[0].cls+=' fc-first';
9947                 row.cn[0].cn[0].style = 'min-height:90px';
9948                 row.cn[6].cls+=' fc-last';
9949                 ret.push(row);
9950                 
9951             }
9952             ret[0].cls += ' fc-first';
9953             ret[4].cls += ' fc-prev-last';
9954             ret[5].cls += ' fc-last';
9955             return ret;
9956             
9957         };
9958         
9959         var cal_table = {
9960             tag: 'table',
9961             cls: 'fc-border-separate',
9962             style : 'width:100%',
9963             cellspacing  : 0,
9964             cn : [
9965                 { 
9966                     tag: 'thead',
9967                     cn : [
9968                         { 
9969                             tag: 'tr',
9970                             cls : 'fc-first fc-last',
9971                             cn : cal_heads()
9972                         }
9973                     ]
9974                 },
9975                 { 
9976                     tag: 'tbody',
9977                     cn : cal_rows()
9978                 }
9979                   
9980             ]
9981         };
9982          
9983          var cfg = {
9984             cls : 'fc fc-ltr',
9985             cn : [
9986                 header,
9987                 {
9988                     cls : 'fc-content',
9989                     style : "position: relative;",
9990                     cn : [
9991                         {
9992                             cls : 'fc-view fc-view-month fc-grid',
9993                             style : 'position: relative',
9994                             unselectable : 'on',
9995                             cn : [
9996                                 {
9997                                     cls : 'fc-event-container',
9998                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9999                                 },
10000                                 cal_table
10001                             ]
10002                         }
10003                     ]
10004     
10005                 }
10006            ] 
10007             
10008         };
10009         
10010          
10011         
10012         return cfg;
10013     },
10014     
10015     
10016     initEvents : function()
10017     {
10018         if(!this.store){
10019             throw "can not find store for calendar";
10020         }
10021         
10022         var mark = {
10023             tag: "div",
10024             cls:"x-dlg-mask",
10025             style: "text-align:center",
10026             cn: [
10027                 {
10028                     tag: "div",
10029                     style: "background-color:white;width:50%;margin:250 auto",
10030                     cn: [
10031                         {
10032                             tag: "img",
10033                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10034                         },
10035                         {
10036                             tag: "span",
10037                             html: "Loading"
10038                         }
10039                         
10040                     ]
10041                 }
10042             ]
10043         }
10044         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10045         
10046         var size = this.el.select('.fc-content', true).first().getSize();
10047         this.maskEl.setSize(size.width, size.height);
10048         this.maskEl.enableDisplayMode("block");
10049         if(!this.loadMask){
10050             this.maskEl.hide();
10051         }
10052         
10053         this.store = Roo.factory(this.store, Roo.data);
10054         this.store.on('load', this.onLoad, this);
10055         this.store.on('beforeload', this.onBeforeLoad, this);
10056         
10057         this.resize();
10058         
10059         this.cells = this.el.select('.fc-day',true);
10060         //Roo.log(this.cells);
10061         this.textNodes = this.el.query('.fc-day-number');
10062         this.cells.addClassOnOver('fc-state-hover');
10063         
10064         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10065         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10066         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10067         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10068         
10069         this.on('monthchange', this.onMonthChange, this);
10070         
10071         this.update(new Date().clearTime());
10072     },
10073     
10074     resize : function() {
10075         var sz  = this.el.getSize();
10076         
10077         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10078         this.el.select('.fc-day-content div',true).setHeight(34);
10079     },
10080     
10081     
10082     // private
10083     showPrevMonth : function(e){
10084         this.update(this.activeDate.add("mo", -1));
10085     },
10086     showToday : function(e){
10087         this.update(new Date().clearTime());
10088     },
10089     // private
10090     showNextMonth : function(e){
10091         this.update(this.activeDate.add("mo", 1));
10092     },
10093
10094     // private
10095     showPrevYear : function(){
10096         this.update(this.activeDate.add("y", -1));
10097     },
10098
10099     // private
10100     showNextYear : function(){
10101         this.update(this.activeDate.add("y", 1));
10102     },
10103
10104     
10105    // private
10106     update : function(date)
10107     {
10108         var vd = this.activeDate;
10109         this.activeDate = date;
10110 //        if(vd && this.el){
10111 //            var t = date.getTime();
10112 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10113 //                Roo.log('using add remove');
10114 //                
10115 //                this.fireEvent('monthchange', this, date);
10116 //                
10117 //                this.cells.removeClass("fc-state-highlight");
10118 //                this.cells.each(function(c){
10119 //                   if(c.dateValue == t){
10120 //                       c.addClass("fc-state-highlight");
10121 //                       setTimeout(function(){
10122 //                            try{c.dom.firstChild.focus();}catch(e){}
10123 //                       }, 50);
10124 //                       return false;
10125 //                   }
10126 //                   return true;
10127 //                });
10128 //                return;
10129 //            }
10130 //        }
10131         
10132         var days = date.getDaysInMonth();
10133         
10134         var firstOfMonth = date.getFirstDateOfMonth();
10135         var startingPos = firstOfMonth.getDay()-this.startDay;
10136         
10137         if(startingPos < this.startDay){
10138             startingPos += 7;
10139         }
10140         
10141         var pm = date.add(Date.MONTH, -1);
10142         var prevStart = pm.getDaysInMonth()-startingPos;
10143 //        
10144         this.cells = this.el.select('.fc-day',true);
10145         this.textNodes = this.el.query('.fc-day-number');
10146         this.cells.addClassOnOver('fc-state-hover');
10147         
10148         var cells = this.cells.elements;
10149         var textEls = this.textNodes;
10150         
10151         Roo.each(cells, function(cell){
10152             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10153         });
10154         
10155         days += startingPos;
10156
10157         // convert everything to numbers so it's fast
10158         var day = 86400000;
10159         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10160         //Roo.log(d);
10161         //Roo.log(pm);
10162         //Roo.log(prevStart);
10163         
10164         var today = new Date().clearTime().getTime();
10165         var sel = date.clearTime().getTime();
10166         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10167         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10168         var ddMatch = this.disabledDatesRE;
10169         var ddText = this.disabledDatesText;
10170         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10171         var ddaysText = this.disabledDaysText;
10172         var format = this.format;
10173         
10174         var setCellClass = function(cal, cell){
10175             
10176             //Roo.log('set Cell Class');
10177             cell.title = "";
10178             var t = d.getTime();
10179             
10180             //Roo.log(d);
10181             
10182             cell.dateValue = t;
10183             if(t == today){
10184                 cell.className += " fc-today";
10185                 cell.className += " fc-state-highlight";
10186                 cell.title = cal.todayText;
10187             }
10188             if(t == sel){
10189                 // disable highlight in other month..
10190                 //cell.className += " fc-state-highlight";
10191                 
10192             }
10193             // disabling
10194             if(t < min) {
10195                 cell.className = " fc-state-disabled";
10196                 cell.title = cal.minText;
10197                 return;
10198             }
10199             if(t > max) {
10200                 cell.className = " fc-state-disabled";
10201                 cell.title = cal.maxText;
10202                 return;
10203             }
10204             if(ddays){
10205                 if(ddays.indexOf(d.getDay()) != -1){
10206                     cell.title = ddaysText;
10207                     cell.className = " fc-state-disabled";
10208                 }
10209             }
10210             if(ddMatch && format){
10211                 var fvalue = d.dateFormat(format);
10212                 if(ddMatch.test(fvalue)){
10213                     cell.title = ddText.replace("%0", fvalue);
10214                     cell.className = " fc-state-disabled";
10215                 }
10216             }
10217             
10218             if (!cell.initialClassName) {
10219                 cell.initialClassName = cell.dom.className;
10220             }
10221             
10222             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10223         };
10224
10225         var i = 0;
10226         
10227         for(; i < startingPos; i++) {
10228             textEls[i].innerHTML = (++prevStart);
10229             d.setDate(d.getDate()+1);
10230             
10231             cells[i].className = "fc-past fc-other-month";
10232             setCellClass(this, cells[i]);
10233         }
10234         
10235         var intDay = 0;
10236         
10237         for(; i < days; i++){
10238             intDay = i - startingPos + 1;
10239             textEls[i].innerHTML = (intDay);
10240             d.setDate(d.getDate()+1);
10241             
10242             cells[i].className = ''; // "x-date-active";
10243             setCellClass(this, cells[i]);
10244         }
10245         var extraDays = 0;
10246         
10247         for(; i < 42; i++) {
10248             textEls[i].innerHTML = (++extraDays);
10249             d.setDate(d.getDate()+1);
10250             
10251             cells[i].className = "fc-future fc-other-month";
10252             setCellClass(this, cells[i]);
10253         }
10254         
10255         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10256         
10257         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10258         
10259         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10260         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10261         
10262         if(totalRows != 6){
10263             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10264             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10265         }
10266         
10267         this.fireEvent('monthchange', this, date);
10268         
10269         
10270         /*
10271         if(!this.internalRender){
10272             var main = this.el.dom.firstChild;
10273             var w = main.offsetWidth;
10274             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10275             Roo.fly(main).setWidth(w);
10276             this.internalRender = true;
10277             // opera does not respect the auto grow header center column
10278             // then, after it gets a width opera refuses to recalculate
10279             // without a second pass
10280             if(Roo.isOpera && !this.secondPass){
10281                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10282                 this.secondPass = true;
10283                 this.update.defer(10, this, [date]);
10284             }
10285         }
10286         */
10287         
10288     },
10289     
10290     findCell : function(dt) {
10291         dt = dt.clearTime().getTime();
10292         var ret = false;
10293         this.cells.each(function(c){
10294             //Roo.log("check " +c.dateValue + '?=' + dt);
10295             if(c.dateValue == dt){
10296                 ret = c;
10297                 return false;
10298             }
10299             return true;
10300         });
10301         
10302         return ret;
10303     },
10304     
10305     findCells : function(ev) {
10306         var s = ev.start.clone().clearTime().getTime();
10307        // Roo.log(s);
10308         var e= ev.end.clone().clearTime().getTime();
10309        // Roo.log(e);
10310         var ret = [];
10311         this.cells.each(function(c){
10312              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10313             
10314             if(c.dateValue > e){
10315                 return ;
10316             }
10317             if(c.dateValue < s){
10318                 return ;
10319             }
10320             ret.push(c);
10321         });
10322         
10323         return ret;    
10324     },
10325     
10326     findBestRow: function(cells)
10327     {
10328         var ret = 0;
10329         
10330         for (var i =0 ; i < cells.length;i++) {
10331             ret  = Math.max(cells[i].rows || 0,ret);
10332         }
10333         return ret;
10334         
10335     },
10336     
10337     
10338     addItem : function(ev)
10339     {
10340         // look for vertical location slot in
10341         var cells = this.findCells(ev);
10342         
10343         ev.row = this.findBestRow(cells);
10344         
10345         // work out the location.
10346         
10347         var crow = false;
10348         var rows = [];
10349         for(var i =0; i < cells.length; i++) {
10350             if (!crow) {
10351                 crow = {
10352                     start : cells[i],
10353                     end :  cells[i]
10354                 };
10355                 continue;
10356             }
10357             if (crow.start.getY() == cells[i].getY()) {
10358                 // on same row.
10359                 crow.end = cells[i];
10360                 continue;
10361             }
10362             // different row.
10363             rows.push(crow);
10364             crow = {
10365                 start: cells[i],
10366                 end : cells[i]
10367             };
10368             
10369         }
10370         
10371         rows.push(crow);
10372         ev.els = [];
10373         ev.rows = rows;
10374         ev.cells = cells;
10375         for (var i = 0; i < cells.length;i++) {
10376             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10377             
10378         }
10379         
10380         this.calevents.push(ev);
10381     },
10382     
10383     clearEvents: function() {
10384         
10385         if(!this.calevents){
10386             return;
10387         }
10388         
10389         Roo.each(this.cells.elements, function(c){
10390             c.rows = 0;
10391         });
10392         
10393         Roo.each(this.calevents, function(e) {
10394             Roo.each(e.els, function(el) {
10395                 el.un('mouseenter' ,this.onEventEnter, this);
10396                 el.un('mouseleave' ,this.onEventLeave, this);
10397                 el.remove();
10398             },this);
10399         },this);
10400         
10401     },
10402     
10403     renderEvents: function()
10404     {   
10405         // first make sure there is enough space..
10406         
10407         this.cells.each(function(c) {
10408         
10409             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10410         });
10411         
10412         for (var e = 0; e < this.calevents.length; e++) {
10413             var ev = this.calevents[e];
10414             var cells = ev.cells;
10415             var rows = ev.rows;
10416             
10417             for(var i =0; i < rows.length; i++) {
10418                 
10419                  
10420                 // how many rows should it span..
10421                 
10422                 var  cfg = {
10423                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10424                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10425                     
10426                     unselectable : "on",
10427                     cn : [
10428                         {
10429                             cls: 'fc-event-inner',
10430                             cn : [
10431 //                                {
10432 //                                  tag:'span',
10433 //                                  cls: 'fc-event-time',
10434 //                                  html : cells.length > 1 ? '' : ev.time
10435 //                                },
10436                                 {
10437                                   tag:'span',
10438                                   cls: 'fc-event-title',
10439                                   html : String.format('{0}', ev.title)
10440                                 }
10441                                 
10442                                 
10443                             ]
10444                         },
10445                         {
10446                             cls: 'ui-resizable-handle ui-resizable-e',
10447                             html : '&nbsp;&nbsp;&nbsp'
10448                         }
10449                         
10450                     ]
10451                 };
10452                 if (i == 0) {
10453                     cfg.cls += ' fc-event-start';
10454                 }
10455                 if ((i+1) == rows.length) {
10456                     cfg.cls += ' fc-event-end';
10457                 }
10458                 
10459                 var ctr = this.el.select('.fc-event-container',true).first();
10460                 var cg = ctr.createChild(cfg);
10461                 
10462                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10463                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10464                 cg.on('click', this.onEventClick, this, ev);
10465                 
10466                 ev.els.push(cg);
10467                 
10468                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10469                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10470                 //Roo.log(cg);
10471                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10472                 cg.setWidth(ebox.right - sbox.x -2);
10473             }
10474             
10475             
10476         }
10477         
10478     },
10479     
10480     onEventEnter: function (e, el,event,d) {
10481         this.fireEvent('evententer', this, el, event);
10482     },
10483     
10484     onEventLeave: function (e, el,event,d) {
10485         this.fireEvent('eventleave', this, el, event);
10486     },
10487     
10488     onEventClick: function (e, el,event,d) {
10489         this.fireEvent('eventclick', this, el, event);
10490     },
10491     
10492     onMonthChange: function () {
10493         this.store.load();
10494     },
10495     
10496     onLoad: function () 
10497     {   
10498         this.calevents = [];
10499         var cal = this;
10500         
10501         if(this.store.getCount() > 0){
10502             this.store.data.each(function(d){
10503                cal.addItem({
10504                     id : d.data.id,
10505                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10506                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10507                     time : d.data.start_time,
10508                     title : d.data.title,
10509                     description : d.data.description,
10510                     venue : d.data.venue
10511                 });
10512             });
10513         }
10514         
10515         this.renderEvents();
10516         
10517         if(this.loadMask){
10518             this.maskEl.hide();
10519         }
10520     },
10521     
10522     onBeforeLoad: function()
10523     {
10524         this.clearEvents();
10525         
10526         if(this.loadMask){
10527             this.maskEl.show();
10528         }
10529     }
10530 });
10531
10532  
10533  /*
10534  * - LGPL
10535  *
10536  * element
10537  * 
10538  */
10539
10540 /**
10541  * @class Roo.bootstrap.Popover
10542  * @extends Roo.bootstrap.Component
10543  * Bootstrap Popover class
10544  * @cfg {String} html contents of the popover   (or false to use children..)
10545  * @cfg {String} title of popover (or false to hide)
10546  * @cfg {String} placement how it is placed
10547  * @cfg {String} trigger click || hover (or false to trigger manually)
10548  * @cfg {String} over what (parent or false to trigger manually.)
10549  * 
10550  * @constructor
10551  * Create a new Popover
10552  * @param {Object} config The config object
10553  */
10554
10555 Roo.bootstrap.Popover = function(config){
10556     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10557 };
10558
10559 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10560     
10561     title: 'Fill in a title',
10562     html: false,
10563     
10564     placement : 'right',
10565     trigger : 'hover', // hover
10566     
10567     over: 'parent',
10568     
10569     can_build_overlaid : false,
10570     
10571     getChildContainer : function()
10572     {
10573         return this.el.select('.popover-content',true).first();
10574     },
10575     
10576     getAutoCreate : function(){
10577          Roo.log('make popover?');
10578         var cfg = {
10579            cls : 'popover roo-dynamic',
10580            style: 'display:block',
10581            cn : [
10582                 {
10583                     cls : 'arrow'
10584                 },
10585                 {
10586                     cls : 'popover-inner',
10587                     cn : [
10588                         {
10589                             tag: 'h3',
10590                             cls: 'popover-title',
10591                             html : this.title
10592                         },
10593                         {
10594                             cls : 'popover-content',
10595                             html : this.html
10596                         }
10597                     ]
10598                     
10599                 }
10600            ]
10601         };
10602         
10603         return cfg;
10604     },
10605     setTitle: function(str)
10606     {
10607         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10608     },
10609     setContent: function(str)
10610     {
10611         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10612     },
10613     // as it get's added to the bottom of the page.
10614     onRender : function(ct, position)
10615     {
10616         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10617         if(!this.el){
10618             var cfg = Roo.apply({},  this.getAutoCreate());
10619             cfg.id = Roo.id();
10620             
10621             if (this.cls) {
10622                 cfg.cls += ' ' + this.cls;
10623             }
10624             if (this.style) {
10625                 cfg.style = this.style;
10626             }
10627             Roo.log("adding to ")
10628             this.el = Roo.get(document.body).createChild(cfg, position);
10629             Roo.log(this.el);
10630         }
10631         this.initEvents();
10632     },
10633     
10634     initEvents : function()
10635     {
10636         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10637         this.el.enableDisplayMode('block');
10638         this.el.hide();
10639         if (this.over === false) {
10640             return; 
10641         }
10642         if (this.triggers === false) {
10643             return;
10644         }
10645         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10646         var triggers = this.trigger ? this.trigger.split(' ') : [];
10647         Roo.each(triggers, function(trigger) {
10648         
10649             if (trigger == 'click') {
10650                 on_el.on('click', this.toggle, this);
10651             } else if (trigger != 'manual') {
10652                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10653                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10654       
10655                 on_el.on(eventIn  ,this.enter, this);
10656                 on_el.on(eventOut, this.leave, this);
10657             }
10658         }, this);
10659         
10660     },
10661     
10662     
10663     // private
10664     timeout : null,
10665     hoverState : null,
10666     
10667     toggle : function () {
10668         this.hoverState == 'in' ? this.leave() : this.enter();
10669     },
10670     
10671     enter : function () {
10672        
10673     
10674         clearTimeout(this.timeout);
10675     
10676         this.hoverState = 'in'
10677     
10678         if (!this.delay || !this.delay.show) {
10679             this.show();
10680             return 
10681         }
10682         var _t = this;
10683         this.timeout = setTimeout(function () {
10684             if (_t.hoverState == 'in') {
10685                 _t.show();
10686             }
10687         }, this.delay.show)
10688     },
10689     leave : function() {
10690         clearTimeout(this.timeout);
10691     
10692         this.hoverState = 'out'
10693     
10694         if (!this.delay || !this.delay.hide) {
10695             this.hide();
10696             return 
10697         }
10698         var _t = this;
10699         this.timeout = setTimeout(function () {
10700             if (_t.hoverState == 'out') {
10701                 _t.hide();
10702             }
10703         }, this.delay.hide)
10704     },
10705     
10706     show : function (on_el)
10707     {
10708         if (!on_el) {
10709             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10710         }
10711         // set content.
10712         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10713         if (this.html !== false) {
10714             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10715         }
10716         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10717         if (!this.title.length) {
10718             this.el.select('.popover-title',true).hide();
10719         }
10720         
10721         var placement = typeof this.placement == 'function' ?
10722             this.placement.call(this, this.el, on_el) :
10723             this.placement;
10724             
10725         var autoToken = /\s?auto?\s?/i;
10726         var autoPlace = autoToken.test(placement);
10727         if (autoPlace) {
10728             placement = placement.replace(autoToken, '') || 'top';
10729         }
10730         
10731         //this.el.detach()
10732         //this.el.setXY([0,0]);
10733         this.el.show();
10734         this.el.dom.style.display='block';
10735         this.el.addClass(placement);
10736         
10737         //this.el.appendTo(on_el);
10738         
10739         var p = this.getPosition();
10740         var box = this.el.getBox();
10741         
10742         if (autoPlace) {
10743             // fixme..
10744         }
10745         var align = Roo.bootstrap.Popover.alignment[placement]
10746         this.el.alignTo(on_el, align[0],align[1]);
10747         //var arrow = this.el.select('.arrow',true).first();
10748         //arrow.set(align[2], 
10749         
10750         this.el.addClass('in');
10751         this.hoverState = null;
10752         
10753         if (this.el.hasClass('fade')) {
10754             // fade it?
10755         }
10756         
10757     },
10758     hide : function()
10759     {
10760         this.el.setXY([0,0]);
10761         this.el.removeClass('in');
10762         this.el.hide();
10763         
10764     }
10765     
10766 });
10767
10768 Roo.bootstrap.Popover.alignment = {
10769     'left' : ['r-l', [-10,0], 'right'],
10770     'right' : ['l-r', [10,0], 'left'],
10771     'bottom' : ['t-b', [0,10], 'top'],
10772     'top' : [ 'b-t', [0,-10], 'bottom']
10773 };
10774
10775  /*
10776  * - LGPL
10777  *
10778  * Progress
10779  * 
10780  */
10781
10782 /**
10783  * @class Roo.bootstrap.Progress
10784  * @extends Roo.bootstrap.Component
10785  * Bootstrap Progress class
10786  * @cfg {Boolean} striped striped of the progress bar
10787  * @cfg {Boolean} active animated of the progress bar
10788  * 
10789  * 
10790  * @constructor
10791  * Create a new Progress
10792  * @param {Object} config The config object
10793  */
10794
10795 Roo.bootstrap.Progress = function(config){
10796     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10797 };
10798
10799 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10800     
10801     striped : false,
10802     active: false,
10803     
10804     getAutoCreate : function(){
10805         var cfg = {
10806             tag: 'div',
10807             cls: 'progress'
10808         };
10809         
10810         
10811         if(this.striped){
10812             cfg.cls += ' progress-striped';
10813         }
10814       
10815         if(this.active){
10816             cfg.cls += ' active';
10817         }
10818         
10819         
10820         return cfg;
10821     }
10822    
10823 });
10824
10825  
10826
10827  /*
10828  * - LGPL
10829  *
10830  * ProgressBar
10831  * 
10832  */
10833
10834 /**
10835  * @class Roo.bootstrap.ProgressBar
10836  * @extends Roo.bootstrap.Component
10837  * Bootstrap ProgressBar class
10838  * @cfg {Number} aria_valuenow aria-value now
10839  * @cfg {Number} aria_valuemin aria-value min
10840  * @cfg {Number} aria_valuemax aria-value max
10841  * @cfg {String} label label for the progress bar
10842  * @cfg {String} panel (success | info | warning | danger )
10843  * @cfg {String} role role of the progress bar
10844  * @cfg {String} sr_only text
10845  * 
10846  * 
10847  * @constructor
10848  * Create a new ProgressBar
10849  * @param {Object} config The config object
10850  */
10851
10852 Roo.bootstrap.ProgressBar = function(config){
10853     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10854 };
10855
10856 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10857     
10858     aria_valuenow : 0,
10859     aria_valuemin : 0,
10860     aria_valuemax : 100,
10861     label : false,
10862     panel : false,
10863     role : false,
10864     sr_only: false,
10865     
10866     getAutoCreate : function()
10867     {
10868         
10869         var cfg = {
10870             tag: 'div',
10871             cls: 'progress-bar',
10872             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10873         };
10874         
10875         if(this.sr_only){
10876             cfg.cn = {
10877                 tag: 'span',
10878                 cls: 'sr-only',
10879                 html: this.sr_only
10880             }
10881         }
10882         
10883         if(this.role){
10884             cfg.role = this.role;
10885         }
10886         
10887         if(this.aria_valuenow){
10888             cfg['aria-valuenow'] = this.aria_valuenow;
10889         }
10890         
10891         if(this.aria_valuemin){
10892             cfg['aria-valuemin'] = this.aria_valuemin;
10893         }
10894         
10895         if(this.aria_valuemax){
10896             cfg['aria-valuemax'] = this.aria_valuemax;
10897         }
10898         
10899         if(this.label && !this.sr_only){
10900             cfg.html = this.label;
10901         }
10902         
10903         if(this.panel){
10904             cfg.cls += ' progress-bar-' + this.panel;
10905         }
10906         
10907         return cfg;
10908     },
10909     
10910     update : function(aria_valuenow)
10911     {
10912         this.aria_valuenow = aria_valuenow;
10913         
10914         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10915     }
10916    
10917 });
10918
10919  
10920
10921  /*
10922  * - LGPL
10923  *
10924  * TabPanel
10925  * 
10926  */
10927
10928 /**
10929  * @class Roo.bootstrap.TabPanel
10930  * @extends Roo.bootstrap.Component
10931  * Bootstrap TabPanel class
10932  * @cfg {Boolean} active panel active
10933  * @cfg {String} html panel content
10934  * @cfg {String} tabId tab relate id
10935  * 
10936  * 
10937  * @constructor
10938  * Create a new TabPanel
10939  * @param {Object} config The config object
10940  */
10941
10942 Roo.bootstrap.TabPanel = function(config){
10943     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10944 };
10945
10946 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10947     
10948     active: false,
10949     html: false,
10950     tabId: false,
10951     
10952     getAutoCreate : function(){
10953         var cfg = {
10954             tag: 'div',
10955             cls: 'tab-pane',
10956             html: this.html || ''
10957         };
10958         
10959         if(this.active){
10960             cfg.cls += ' active';
10961         }
10962         
10963         if(this.tabId){
10964             cfg.tabId = this.tabId;
10965         }
10966         
10967         return cfg;
10968     }
10969    
10970 });
10971
10972  
10973
10974  /*
10975  * - LGPL
10976  *
10977  * DateField
10978  * 
10979  */
10980
10981 /**
10982  * @class Roo.bootstrap.DateField
10983  * @extends Roo.bootstrap.Input
10984  * Bootstrap DateField class
10985  * @cfg {Number} weekStart default 0
10986  * @cfg {Number} weekStart default 0
10987  * @cfg {Number} viewMode default empty, (months|years)
10988  * @cfg {Number} minViewMode default empty, (months|years)
10989  * @cfg {Number} startDate default -Infinity
10990  * @cfg {Number} endDate default Infinity
10991  * @cfg {Boolean} todayHighlight default false
10992  * @cfg {Boolean} todayBtn default false
10993  * @cfg {Boolean} calendarWeeks default false
10994  * @cfg {Object} daysOfWeekDisabled default empty
10995  * 
10996  * @cfg {Boolean} keyboardNavigation default true
10997  * @cfg {String} language default en
10998  * 
10999  * @constructor
11000  * Create a new DateField
11001  * @param {Object} config The config object
11002  */
11003
11004 Roo.bootstrap.DateField = function(config){
11005     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11006      this.addEvents({
11007             /**
11008              * @event show
11009              * Fires when this field show.
11010              * @param {Roo.bootstrap.DateField} this
11011              * @param {Mixed} date The date value
11012              */
11013             show : true,
11014             /**
11015              * @event show
11016              * Fires when this field hide.
11017              * @param {Roo.bootstrap.DateField} this
11018              * @param {Mixed} date The date value
11019              */
11020             hide : true,
11021             /**
11022              * @event select
11023              * Fires when select a date.
11024              * @param {Roo.bootstrap.DateField} this
11025              * @param {Mixed} date The date value
11026              */
11027             select : true
11028         });
11029 };
11030
11031 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11032     
11033     /**
11034      * @cfg {String} format
11035      * The default date format string which can be overriden for localization support.  The format must be
11036      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11037      */
11038     format : "m/d/y",
11039     /**
11040      * @cfg {String} altFormats
11041      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11042      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11043      */
11044     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11045     
11046     weekStart : 0,
11047     
11048     viewMode : '',
11049     
11050     minViewMode : '',
11051     
11052     todayHighlight : false,
11053     
11054     todayBtn: false,
11055     
11056     language: 'en',
11057     
11058     keyboardNavigation: true,
11059     
11060     calendarWeeks: false,
11061     
11062     startDate: -Infinity,
11063     
11064     endDate: Infinity,
11065     
11066     daysOfWeekDisabled: [],
11067     
11068     _events: [],
11069     
11070     UTCDate: function()
11071     {
11072         return new Date(Date.UTC.apply(Date, arguments));
11073     },
11074     
11075     UTCToday: function()
11076     {
11077         var today = new Date();
11078         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11079     },
11080     
11081     getDate: function() {
11082             var d = this.getUTCDate();
11083             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11084     },
11085     
11086     getUTCDate: function() {
11087             return this.date;
11088     },
11089     
11090     setDate: function(d) {
11091             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11092     },
11093     
11094     setUTCDate: function(d) {
11095             this.date = d;
11096             this.setValue(this.formatDate(this.date));
11097     },
11098         
11099     onRender: function(ct, position)
11100     {
11101         
11102         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11103         
11104         this.language = this.language || 'en';
11105         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11106         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11107         
11108         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11109         this.format = this.format || 'm/d/y';
11110         this.isInline = false;
11111         this.isInput = true;
11112         this.component = this.el.select('.add-on', true).first() || false;
11113         this.component = (this.component && this.component.length === 0) ? false : this.component;
11114         this.hasInput = this.component && this.inputEL().length;
11115         
11116         if (typeof(this.minViewMode === 'string')) {
11117             switch (this.minViewMode) {
11118                 case 'months':
11119                     this.minViewMode = 1;
11120                     break;
11121                 case 'years':
11122                     this.minViewMode = 2;
11123                     break;
11124                 default:
11125                     this.minViewMode = 0;
11126                     break;
11127             }
11128         }
11129         
11130         if (typeof(this.viewMode === 'string')) {
11131             switch (this.viewMode) {
11132                 case 'months':
11133                     this.viewMode = 1;
11134                     break;
11135                 case 'years':
11136                     this.viewMode = 2;
11137                     break;
11138                 default:
11139                     this.viewMode = 0;
11140                     break;
11141             }
11142         }
11143                 
11144         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11145         
11146         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11147         
11148         this.picker().on('mousedown', this.onMousedown, this);
11149         this.picker().on('click', this.onClick, this);
11150         
11151         this.picker().addClass('datepicker-dropdown');
11152         
11153         this.startViewMode = this.viewMode;
11154         
11155         
11156         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11157             if(!this.calendarWeeks){
11158                 v.remove();
11159                 return;
11160             };
11161             
11162             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11163             v.attr('colspan', function(i, val){
11164                 return parseInt(val) + 1;
11165             });
11166         })
11167                         
11168         
11169         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11170         
11171         this.setStartDate(this.startDate);
11172         this.setEndDate(this.endDate);
11173         
11174         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11175         
11176         this.fillDow();
11177         this.fillMonths();
11178         this.update();
11179         this.showMode();
11180         
11181         if(this.isInline) {
11182             this.show();
11183         }
11184     },
11185     
11186     picker : function()
11187     {
11188         return this.el.select('.datepicker', true).first();
11189     },
11190     
11191     fillDow: function()
11192     {
11193         var dowCnt = this.weekStart;
11194         
11195         var dow = {
11196             tag: 'tr',
11197             cn: [
11198                 
11199             ]
11200         };
11201         
11202         if(this.calendarWeeks){
11203             dow.cn.push({
11204                 tag: 'th',
11205                 cls: 'cw',
11206                 html: '&nbsp;'
11207             })
11208         }
11209         
11210         while (dowCnt < this.weekStart + 7) {
11211             dow.cn.push({
11212                 tag: 'th',
11213                 cls: 'dow',
11214                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11215             });
11216         }
11217         
11218         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11219     },
11220     
11221     fillMonths: function()
11222     {    
11223         var i = 0
11224         var months = this.picker().select('>.datepicker-months td', true).first();
11225         
11226         months.dom.innerHTML = '';
11227         
11228         while (i < 12) {
11229             var month = {
11230                 tag: 'span',
11231                 cls: 'month',
11232                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11233             }
11234             
11235             months.createChild(month);
11236         }
11237         
11238     },
11239     
11240     update: function(){
11241         
11242         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11243         
11244         if (this.date < this.startDate) {
11245             this.viewDate = new Date(this.startDate);
11246         } else if (this.date > this.endDate) {
11247             this.viewDate = new Date(this.endDate);
11248         } else {
11249             this.viewDate = new Date(this.date);
11250         }
11251         
11252         this.fill();
11253     },
11254     
11255     fill: function() {
11256         var d = new Date(this.viewDate),
11257                 year = d.getUTCFullYear(),
11258                 month = d.getUTCMonth(),
11259                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11260                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11261                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11262                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11263                 currentDate = this.date && this.date.valueOf(),
11264                 today = this.UTCToday();
11265         
11266         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11267         
11268 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11269         
11270 //        this.picker.select('>tfoot th.today').
11271 //                                              .text(dates[this.language].today)
11272 //                                              .toggle(this.todayBtn !== false);
11273     
11274         this.updateNavArrows();
11275         this.fillMonths();
11276                                                 
11277         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11278         
11279         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11280          
11281         prevMonth.setUTCDate(day);
11282         
11283         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11284         
11285         var nextMonth = new Date(prevMonth);
11286         
11287         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11288         
11289         nextMonth = nextMonth.valueOf();
11290         
11291         var fillMonths = false;
11292         
11293         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11294         
11295         while(prevMonth.valueOf() < nextMonth) {
11296             var clsName = '';
11297             
11298             if (prevMonth.getUTCDay() === this.weekStart) {
11299                 if(fillMonths){
11300                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11301                 }
11302                     
11303                 fillMonths = {
11304                     tag: 'tr',
11305                     cn: []
11306                 };
11307                 
11308                 if(this.calendarWeeks){
11309                     // ISO 8601: First week contains first thursday.
11310                     // ISO also states week starts on Monday, but we can be more abstract here.
11311                     var
11312                     // Start of current week: based on weekstart/current date
11313                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11314                     // Thursday of this week
11315                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11316                     // First Thursday of year, year from thursday
11317                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11318                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11319                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11320                     
11321                     fillMonths.cn.push({
11322                         tag: 'td',
11323                         cls: 'cw',
11324                         html: calWeek
11325                     });
11326                 }
11327             }
11328             
11329             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11330                 clsName += ' old';
11331             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11332                 clsName += ' new';
11333             }
11334             if (this.todayHighlight &&
11335                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11336                 prevMonth.getUTCMonth() == today.getMonth() &&
11337                 prevMonth.getUTCDate() == today.getDate()) {
11338                 clsName += ' today';
11339             }
11340             
11341             if (currentDate && prevMonth.valueOf() === currentDate) {
11342                 clsName += ' active';
11343             }
11344             
11345             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11346                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11347                     clsName += ' disabled';
11348             }
11349             
11350             fillMonths.cn.push({
11351                 tag: 'td',
11352                 cls: 'day ' + clsName,
11353                 html: prevMonth.getDate()
11354             })
11355             
11356             prevMonth.setDate(prevMonth.getDate()+1);
11357         }
11358           
11359         var currentYear = this.date && this.date.getUTCFullYear();
11360         var currentMonth = this.date && this.date.getUTCMonth();
11361         
11362         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11363         
11364         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11365             v.removeClass('active');
11366             
11367             if(currentYear === year && k === currentMonth){
11368                 v.addClass('active');
11369             }
11370             
11371             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11372                 v.addClass('disabled');
11373             }
11374             
11375         });
11376         
11377         
11378         year = parseInt(year/10, 10) * 10;
11379         
11380         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11381         
11382         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11383         
11384         year -= 1;
11385         for (var i = -1; i < 11; i++) {
11386             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11387                 tag: 'span',
11388                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11389                 html: year
11390             })
11391             
11392             year += 1;
11393         }
11394     },
11395     
11396     showMode: function(dir) {
11397         if (dir) {
11398             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11399         }
11400         Roo.each(this.picker().select('>div',true).elements, function(v){
11401             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11402             v.hide();
11403         });
11404         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11405     },
11406     
11407     place: function()
11408     {
11409         if(this.isInline) return;
11410         
11411         this.picker().removeClass(['bottom', 'top']);
11412         
11413         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11414             /*
11415              * place to the top of element!
11416              *
11417              */
11418             
11419             this.picker().addClass('top');
11420             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11421             
11422             return;
11423         }
11424         
11425         this.picker().addClass('bottom');
11426         
11427         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11428     },
11429     
11430     parseDate : function(value){
11431         if(!value || value instanceof Date){
11432             return value;
11433         }
11434         var v = Date.parseDate(value, this.format);
11435         if (!v && this.useIso) {
11436             v = Date.parseDate(value, 'Y-m-d');
11437         }
11438         if(!v && this.altFormats){
11439             if(!this.altFormatsArray){
11440                 this.altFormatsArray = this.altFormats.split("|");
11441             }
11442             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11443                 v = Date.parseDate(value, this.altFormatsArray[i]);
11444             }
11445         }
11446         return v;
11447     },
11448     
11449     formatDate : function(date, fmt){
11450         return (!date || !(date instanceof Date)) ?
11451         date : date.dateFormat(fmt || this.format);
11452     },
11453     
11454     onFocus : function()
11455     {
11456         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11457         this.show();
11458     },
11459     
11460     onBlur : function()
11461     {
11462         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11463         this.hide();
11464     },
11465     
11466     show : function()
11467     {
11468         this.picker().show();
11469         this.update();
11470         this.place();
11471         
11472         this.fireEvent('show', this, this.date);
11473     },
11474     
11475     hide : function()
11476     {
11477         if(this.isInline) return;
11478         this.picker().hide();
11479         this.viewMode = this.startViewMode;
11480         this.showMode();
11481         
11482         this.fireEvent('hide', this, this.date);
11483         
11484     },
11485     
11486     onMousedown: function(e){
11487         e.stopPropagation();
11488         e.preventDefault();
11489     },
11490     
11491     keyup: function(e){
11492         Roo.bootstrap.DateField.superclass.keyup.call(this);
11493         this.update();
11494         
11495     },
11496
11497     setValue: function(v){
11498         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11499         
11500         this.fireEvent('select', this, this.date);
11501         
11502     },
11503     
11504     fireKey: function(e){
11505         if (!this.picker().isVisible()){
11506             if (e.keyCode == 27) // allow escape to hide and re-show picker
11507                 this.show();
11508             return;
11509         }
11510         var dateChanged = false,
11511         dir, day, month,
11512         newDate, newViewDate;
11513         switch(e.keyCode){
11514             case 27: // escape
11515                 this.hide();
11516                 e.preventDefault();
11517                 break;
11518             case 37: // left
11519             case 39: // right
11520                 if (!this.keyboardNavigation) break;
11521                 dir = e.keyCode == 37 ? -1 : 1;
11522                 
11523                 if (e.ctrlKey){
11524                     newDate = this.moveYear(this.date, dir);
11525                     newViewDate = this.moveYear(this.viewDate, dir);
11526                 } else if (e.shiftKey){
11527                     newDate = this.moveMonth(this.date, dir);
11528                     newViewDate = this.moveMonth(this.viewDate, dir);
11529                 } else {
11530                     newDate = new Date(this.date);
11531                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11532                     newViewDate = new Date(this.viewDate);
11533                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11534                 }
11535                 if (this.dateWithinRange(newDate)){
11536                     this.date = newDate;
11537                     this.viewDate = newViewDate;
11538                     this.setValue(this.formatDate(this.date));
11539                     this.update();
11540                     e.preventDefault();
11541                     dateChanged = true;
11542                 }
11543                 break;
11544             case 38: // up
11545             case 40: // down
11546                 if (!this.keyboardNavigation) break;
11547                 dir = e.keyCode == 38 ? -1 : 1;
11548                 if (e.ctrlKey){
11549                     newDate = this.moveYear(this.date, dir);
11550                     newViewDate = this.moveYear(this.viewDate, dir);
11551                 } else if (e.shiftKey){
11552                     newDate = this.moveMonth(this.date, dir);
11553                     newViewDate = this.moveMonth(this.viewDate, dir);
11554                 } else {
11555                     newDate = new Date(this.date);
11556                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11557                     newViewDate = new Date(this.viewDate);
11558                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11559                 }
11560                 if (this.dateWithinRange(newDate)){
11561                     this.date = newDate;
11562                     this.viewDate = newViewDate;
11563                     this.setValue(this.formatDate(this.date));
11564                     this.update();
11565                     e.preventDefault();
11566                     dateChanged = true;
11567                 }
11568                 break;
11569             case 13: // enter
11570                 this.setValue(this.formatDate(this.date));
11571                 this.hide();
11572                 e.preventDefault();
11573                 break;
11574             case 9: // tab
11575                 this.setValue(this.formatDate(this.date));
11576                 this.hide();
11577                 break;
11578         }
11579     },
11580     
11581     
11582     onClick: function(e) {
11583         e.stopPropagation();
11584         e.preventDefault();
11585         
11586         var target = e.getTarget();
11587         
11588         if(target.nodeName.toLowerCase() === 'i'){
11589             target = Roo.get(target).dom.parentNode;
11590         }
11591         
11592         var nodeName = target.nodeName;
11593         var className = target.className;
11594         var html = target.innerHTML;
11595         
11596         switch(nodeName.toLowerCase()) {
11597             case 'th':
11598                 switch(className) {
11599                     case 'switch':
11600                         this.showMode(1);
11601                         break;
11602                     case 'prev':
11603                     case 'next':
11604                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11605                         switch(this.viewMode){
11606                                 case 0:
11607                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11608                                         break;
11609                                 case 1:
11610                                 case 2:
11611                                         this.viewDate = this.moveYear(this.viewDate, dir);
11612                                         break;
11613                         }
11614                         this.fill();
11615                         break;
11616                     case 'today':
11617                         var date = new Date();
11618                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11619                         this.fill()
11620                         this.setValue(this.formatDate(this.date));
11621                         this.hide();
11622                         break;
11623                 }
11624                 break;
11625             case 'span':
11626                 if (className.indexOf('disabled') === -1) {
11627                     this.viewDate.setUTCDate(1);
11628                     if (className.indexOf('month') !== -1) {
11629                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11630                     } else {
11631                         var year = parseInt(html, 10) || 0;
11632                         this.viewDate.setUTCFullYear(year);
11633                         
11634                     }
11635                     this.showMode(-1);
11636                     this.fill();
11637                 }
11638                 break;
11639                 
11640             case 'td':
11641                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11642                     var day = parseInt(html, 10) || 1;
11643                     var year = this.viewDate.getUTCFullYear(),
11644                         month = this.viewDate.getUTCMonth();
11645
11646                     if (className.indexOf('old') !== -1) {
11647                         if(month === 0 ){
11648                             month = 11;
11649                             year -= 1;
11650                         }else{
11651                             month -= 1;
11652                         }
11653                     } else if (className.indexOf('new') !== -1) {
11654                         if (month == 11) {
11655                             month = 0;
11656                             year += 1;
11657                         } else {
11658                             month += 1;
11659                         }
11660                     }
11661                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11662                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11663                     this.fill();
11664                     this.setValue(this.formatDate(this.date));
11665                     this.hide();
11666                 }
11667                 break;
11668         }
11669     },
11670     
11671     setStartDate: function(startDate){
11672         this.startDate = startDate || -Infinity;
11673         if (this.startDate !== -Infinity) {
11674             this.startDate = this.parseDate(this.startDate);
11675         }
11676         this.update();
11677         this.updateNavArrows();
11678     },
11679
11680     setEndDate: function(endDate){
11681         this.endDate = endDate || Infinity;
11682         if (this.endDate !== Infinity) {
11683             this.endDate = this.parseDate(this.endDate);
11684         }
11685         this.update();
11686         this.updateNavArrows();
11687     },
11688     
11689     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11690         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11691         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11692             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11693         }
11694         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11695             return parseInt(d, 10);
11696         });
11697         this.update();
11698         this.updateNavArrows();
11699     },
11700     
11701     updateNavArrows: function() {
11702         var d = new Date(this.viewDate),
11703         year = d.getUTCFullYear(),
11704         month = d.getUTCMonth();
11705         
11706         Roo.each(this.picker().select('.prev', true).elements, function(v){
11707             v.show();
11708             switch (this.viewMode) {
11709                 case 0:
11710
11711                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11712                         v.hide();
11713                     }
11714                     break;
11715                 case 1:
11716                 case 2:
11717                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11718                         v.hide();
11719                     }
11720                     break;
11721             }
11722         });
11723         
11724         Roo.each(this.picker().select('.next', true).elements, function(v){
11725             v.show();
11726             switch (this.viewMode) {
11727                 case 0:
11728
11729                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11730                         v.hide();
11731                     }
11732                     break;
11733                 case 1:
11734                 case 2:
11735                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11736                         v.hide();
11737                     }
11738                     break;
11739             }
11740         })
11741     },
11742     
11743     moveMonth: function(date, dir){
11744         if (!dir) return date;
11745         var new_date = new Date(date.valueOf()),
11746         day = new_date.getUTCDate(),
11747         month = new_date.getUTCMonth(),
11748         mag = Math.abs(dir),
11749         new_month, test;
11750         dir = dir > 0 ? 1 : -1;
11751         if (mag == 1){
11752             test = dir == -1
11753             // If going back one month, make sure month is not current month
11754             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11755             ? function(){
11756                 return new_date.getUTCMonth() == month;
11757             }
11758             // If going forward one month, make sure month is as expected
11759             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11760             : function(){
11761                 return new_date.getUTCMonth() != new_month;
11762             };
11763             new_month = month + dir;
11764             new_date.setUTCMonth(new_month);
11765             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11766             if (new_month < 0 || new_month > 11)
11767                 new_month = (new_month + 12) % 12;
11768         } else {
11769             // For magnitudes >1, move one month at a time...
11770             for (var i=0; i<mag; i++)
11771                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11772                 new_date = this.moveMonth(new_date, dir);
11773             // ...then reset the day, keeping it in the new month
11774             new_month = new_date.getUTCMonth();
11775             new_date.setUTCDate(day);
11776             test = function(){
11777                 return new_month != new_date.getUTCMonth();
11778             };
11779         }
11780         // Common date-resetting loop -- if date is beyond end of month, make it
11781         // end of month
11782         while (test()){
11783             new_date.setUTCDate(--day);
11784             new_date.setUTCMonth(new_month);
11785         }
11786         return new_date;
11787     },
11788
11789     moveYear: function(date, dir){
11790         return this.moveMonth(date, dir*12);
11791     },
11792
11793     dateWithinRange: function(date){
11794         return date >= this.startDate && date <= this.endDate;
11795     },
11796
11797     
11798     remove: function() {
11799         this.picker().remove();
11800     }
11801    
11802 });
11803
11804 Roo.apply(Roo.bootstrap.DateField,  {
11805     
11806     head : {
11807         tag: 'thead',
11808         cn: [
11809         {
11810             tag: 'tr',
11811             cn: [
11812             {
11813                 tag: 'th',
11814                 cls: 'prev',
11815                 html: '<i class="icon-arrow-left"/>'
11816             },
11817             {
11818                 tag: 'th',
11819                 cls: 'switch',
11820                 colspan: '5'
11821             },
11822             {
11823                 tag: 'th',
11824                 cls: 'next',
11825                 html: '<i class="icon-arrow-right"/>'
11826             }
11827
11828             ]
11829         }
11830         ]
11831     },
11832     
11833     content : {
11834         tag: 'tbody',
11835         cn: [
11836         {
11837             tag: 'tr',
11838             cn: [
11839             {
11840                 tag: 'td',
11841                 colspan: '7'
11842             }
11843             ]
11844         }
11845         ]
11846     },
11847     
11848     footer : {
11849         tag: 'tfoot',
11850         cn: [
11851         {
11852             tag: 'tr',
11853             cn: [
11854             {
11855                 tag: 'th',
11856                 colspan: '7',
11857                 cls: 'today'
11858             }
11859                     
11860             ]
11861         }
11862         ]
11863     },
11864     
11865     dates:{
11866         en: {
11867             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11868             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11869             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11870             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11871             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11872             today: "Today"
11873         }
11874     },
11875     
11876     modes: [
11877     {
11878         clsName: 'days',
11879         navFnc: 'Month',
11880         navStep: 1
11881     },
11882     {
11883         clsName: 'months',
11884         navFnc: 'FullYear',
11885         navStep: 1
11886     },
11887     {
11888         clsName: 'years',
11889         navFnc: 'FullYear',
11890         navStep: 10
11891     }]
11892 });
11893
11894 Roo.apply(Roo.bootstrap.DateField,  {
11895   
11896     template : {
11897         tag: 'div',
11898         cls: 'datepicker dropdown-menu',
11899         cn: [
11900         {
11901             tag: 'div',
11902             cls: 'datepicker-days',
11903             cn: [
11904             {
11905                 tag: 'table',
11906                 cls: 'table-condensed',
11907                 cn:[
11908                 Roo.bootstrap.DateField.head,
11909                 {
11910                     tag: 'tbody'
11911                 },
11912                 Roo.bootstrap.DateField.footer
11913                 ]
11914             }
11915             ]
11916         },
11917         {
11918             tag: 'div',
11919             cls: 'datepicker-months',
11920             cn: [
11921             {
11922                 tag: 'table',
11923                 cls: 'table-condensed',
11924                 cn:[
11925                 Roo.bootstrap.DateField.head,
11926                 Roo.bootstrap.DateField.content,
11927                 Roo.bootstrap.DateField.footer
11928                 ]
11929             }
11930             ]
11931         },
11932         {
11933             tag: 'div',
11934             cls: 'datepicker-years',
11935             cn: [
11936             {
11937                 tag: 'table',
11938                 cls: 'table-condensed',
11939                 cn:[
11940                 Roo.bootstrap.DateField.head,
11941                 Roo.bootstrap.DateField.content,
11942                 Roo.bootstrap.DateField.footer
11943                 ]
11944             }
11945             ]
11946         }
11947         ]
11948     }
11949 });
11950
11951  
11952
11953  /*
11954  * - LGPL
11955  *
11956  * TimeField
11957  * 
11958  */
11959
11960 /**
11961  * @class Roo.bootstrap.TimeField
11962  * @extends Roo.bootstrap.Input
11963  * Bootstrap DateField class
11964  * 
11965  * 
11966  * @constructor
11967  * Create a new TimeField
11968  * @param {Object} config The config object
11969  */
11970
11971 Roo.bootstrap.TimeField = function(config){
11972     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11973     this.addEvents({
11974             /**
11975              * @event show
11976              * Fires when this field show.
11977              * @param {Roo.bootstrap.DateField} this
11978              * @param {Mixed} date The date value
11979              */
11980             show : true,
11981             /**
11982              * @event show
11983              * Fires when this field hide.
11984              * @param {Roo.bootstrap.DateField} this
11985              * @param {Mixed} date The date value
11986              */
11987             hide : true,
11988             /**
11989              * @event select
11990              * Fires when select a date.
11991              * @param {Roo.bootstrap.DateField} this
11992              * @param {Mixed} date The date value
11993              */
11994             select : true
11995         });
11996 };
11997
11998 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11999     
12000     /**
12001      * @cfg {String} format
12002      * The default time format string which can be overriden for localization support.  The format must be
12003      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12004      */
12005     format : "H:i",
12006        
12007     onRender: function(ct, position)
12008     {
12009         
12010         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12011                 
12012         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12013         
12014         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12015         
12016         this.pop = this.picker().select('>.datepicker-time',true).first();
12017         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12018         
12019         this.picker().on('mousedown', this.onMousedown, this);
12020         this.picker().on('click', this.onClick, this);
12021         
12022         this.picker().addClass('datepicker-dropdown');
12023     
12024         this.fillTime();
12025         this.update();
12026             
12027         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12028         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12029         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12030         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12031         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12032         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12033
12034     },
12035     
12036     fireKey: function(e){
12037         if (!this.picker().isVisible()){
12038             if (e.keyCode == 27) // allow escape to hide and re-show picker
12039                 this.show();
12040             return;
12041         }
12042
12043         e.preventDefault();
12044         
12045         switch(e.keyCode){
12046             case 27: // escape
12047                 this.hide();
12048                 break;
12049             case 37: // left
12050             case 39: // right
12051                 this.onTogglePeriod();
12052                 break;
12053             case 38: // up
12054                 this.onIncrementMinutes();
12055                 break;
12056             case 40: // down
12057                 this.onDecrementMinutes();
12058                 break;
12059             case 13: // enter
12060             case 9: // tab
12061                 this.setTime();
12062                 break;
12063         }
12064     },
12065     
12066     onClick: function(e) {
12067         e.stopPropagation();
12068         e.preventDefault();
12069     },
12070     
12071     picker : function()
12072     {
12073         return this.el.select('.datepicker', true).first();
12074     },
12075     
12076     fillTime: function()
12077     {    
12078         var time = this.pop.select('tbody', true).first();
12079         
12080         time.dom.innerHTML = '';
12081         
12082         time.createChild({
12083             tag: 'tr',
12084             cn: [
12085                 {
12086                     tag: 'td',
12087                     cn: [
12088                         {
12089                             tag: 'a',
12090                             href: '#',
12091                             cls: 'btn',
12092                             cn: [
12093                                 {
12094                                     tag: 'span',
12095                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12096                                 }
12097                             ]
12098                         } 
12099                     ]
12100                 },
12101                 {
12102                     tag: 'td',
12103                     cls: 'separator'
12104                 },
12105                 {
12106                     tag: 'td',
12107                     cn: [
12108                         {
12109                             tag: 'a',
12110                             href: '#',
12111                             cls: 'btn',
12112                             cn: [
12113                                 {
12114                                     tag: 'span',
12115                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12116                                 }
12117                             ]
12118                         }
12119                     ]
12120                 },
12121                 {
12122                     tag: 'td',
12123                     cls: 'separator'
12124                 }
12125             ]
12126         });
12127         
12128         time.createChild({
12129             tag: 'tr',
12130             cn: [
12131                 {
12132                     tag: 'td',
12133                     cn: [
12134                         {
12135                             tag: 'span',
12136                             cls: 'timepicker-hour',
12137                             html: '00'
12138                         }  
12139                     ]
12140                 },
12141                 {
12142                     tag: 'td',
12143                     cls: 'separator',
12144                     html: ':'
12145                 },
12146                 {
12147                     tag: 'td',
12148                     cn: [
12149                         {
12150                             tag: 'span',
12151                             cls: 'timepicker-minute',
12152                             html: '00'
12153                         }  
12154                     ]
12155                 },
12156                 {
12157                     tag: 'td',
12158                     cls: 'separator'
12159                 },
12160                 {
12161                     tag: 'td',
12162                     cn: [
12163                         {
12164                             tag: 'button',
12165                             type: 'button',
12166                             cls: 'btn btn-primary period',
12167                             html: 'AM'
12168                             
12169                         }
12170                     ]
12171                 }
12172             ]
12173         });
12174         
12175         time.createChild({
12176             tag: 'tr',
12177             cn: [
12178                 {
12179                     tag: 'td',
12180                     cn: [
12181                         {
12182                             tag: 'a',
12183                             href: '#',
12184                             cls: 'btn',
12185                             cn: [
12186                                 {
12187                                     tag: 'span',
12188                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12189                                 }
12190                             ]
12191                         }
12192                     ]
12193                 },
12194                 {
12195                     tag: 'td',
12196                     cls: 'separator'
12197                 },
12198                 {
12199                     tag: 'td',
12200                     cn: [
12201                         {
12202                             tag: 'a',
12203                             href: '#',
12204                             cls: 'btn',
12205                             cn: [
12206                                 {
12207                                     tag: 'span',
12208                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12209                                 }
12210                             ]
12211                         }
12212                     ]
12213                 },
12214                 {
12215                     tag: 'td',
12216                     cls: 'separator'
12217                 }
12218             ]
12219         });
12220         
12221     },
12222     
12223     update: function()
12224     {
12225         
12226         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12227         
12228         this.fill();
12229     },
12230     
12231     fill: function() 
12232     {
12233         var hours = this.time.getHours();
12234         var minutes = this.time.getMinutes();
12235         var period = 'AM';
12236         
12237         if(hours > 11){
12238             period = 'PM';
12239         }
12240         
12241         if(hours == 0){
12242             hours = 12;
12243         }
12244         
12245         
12246         if(hours > 12){
12247             hours = hours - 12;
12248         }
12249         
12250         if(hours < 10){
12251             hours = '0' + hours;
12252         }
12253         
12254         if(minutes < 10){
12255             minutes = '0' + minutes;
12256         }
12257         
12258         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12259         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12260         this.pop.select('button', true).first().dom.innerHTML = period;
12261         
12262     },
12263     
12264     place: function()
12265     {   
12266         this.picker().removeClass(['bottom', 'top']);
12267         
12268         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12269             /*
12270              * place to the top of element!
12271              *
12272              */
12273             
12274             this.picker().addClass('top');
12275             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12276             
12277             return;
12278         }
12279         
12280         this.picker().addClass('bottom');
12281         
12282         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12283     },
12284   
12285     onFocus : function()
12286     {
12287         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12288         this.show();
12289     },
12290     
12291     onBlur : function()
12292     {
12293         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12294         this.hide();
12295     },
12296     
12297     show : function()
12298     {
12299         this.picker().show();
12300         this.pop.show();
12301         this.update();
12302         this.place();
12303         
12304         this.fireEvent('show', this, this.date);
12305     },
12306     
12307     hide : function()
12308     {
12309         this.picker().hide();
12310         this.pop.hide();
12311         
12312         this.fireEvent('hide', this, this.date);
12313     },
12314     
12315     setTime : function()
12316     {
12317         this.hide();
12318         this.setValue(this.time.format(this.format));
12319         
12320         this.fireEvent('select', this, this.date);
12321         
12322         
12323     },
12324     
12325     onMousedown: function(e){
12326         e.stopPropagation();
12327         e.preventDefault();
12328     },
12329     
12330     onIncrementHours: function()
12331     {
12332         Roo.log('onIncrementHours');
12333         this.time = this.time.add(Date.HOUR, 1);
12334         this.update();
12335         
12336     },
12337     
12338     onDecrementHours: function()
12339     {
12340         Roo.log('onDecrementHours');
12341         this.time = this.time.add(Date.HOUR, -1);
12342         this.update();
12343     },
12344     
12345     onIncrementMinutes: function()
12346     {
12347         Roo.log('onIncrementMinutes');
12348         this.time = this.time.add(Date.MINUTE, 1);
12349         this.update();
12350     },
12351     
12352     onDecrementMinutes: function()
12353     {
12354         Roo.log('onDecrementMinutes');
12355         this.time = this.time.add(Date.MINUTE, -1);
12356         this.update();
12357     },
12358     
12359     onTogglePeriod: function()
12360     {
12361         Roo.log('onTogglePeriod');
12362         this.time = this.time.add(Date.HOUR, 12);
12363         this.update();
12364     }
12365     
12366    
12367 });
12368
12369 Roo.apply(Roo.bootstrap.TimeField,  {
12370     
12371     content : {
12372         tag: 'tbody',
12373         cn: [
12374             {
12375                 tag: 'tr',
12376                 cn: [
12377                 {
12378                     tag: 'td',
12379                     colspan: '7'
12380                 }
12381                 ]
12382             }
12383         ]
12384     },
12385     
12386     footer : {
12387         tag: 'tfoot',
12388         cn: [
12389             {
12390                 tag: 'tr',
12391                 cn: [
12392                 {
12393                     tag: 'th',
12394                     colspan: '7',
12395                     cls: '',
12396                     cn: [
12397                         {
12398                             tag: 'button',
12399                             cls: 'btn btn-info ok',
12400                             html: 'OK'
12401                         }
12402                     ]
12403                 }
12404
12405                 ]
12406             }
12407         ]
12408     }
12409 });
12410
12411 Roo.apply(Roo.bootstrap.TimeField,  {
12412   
12413     template : {
12414         tag: 'div',
12415         cls: 'datepicker dropdown-menu',
12416         cn: [
12417             {
12418                 tag: 'div',
12419                 cls: 'datepicker-time',
12420                 cn: [
12421                 {
12422                     tag: 'table',
12423                     cls: 'table-condensed',
12424                     cn:[
12425                     Roo.bootstrap.TimeField.content,
12426                     Roo.bootstrap.TimeField.footer
12427                     ]
12428                 }
12429                 ]
12430             }
12431         ]
12432     }
12433 });
12434
12435  
12436
12437  /*
12438  * - LGPL
12439  *
12440  * CheckBox
12441  * 
12442  */
12443
12444 /**
12445  * @class Roo.bootstrap.CheckBox
12446  * @extends Roo.bootstrap.Input
12447  * Bootstrap CheckBox class
12448  * 
12449  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12450  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12451  * @cfg {String} boxLabel The text that appears beside the checkbox
12452  * @cfg {Boolean} checked initnal the element
12453  * 
12454  * @constructor
12455  * Create a new CheckBox
12456  * @param {Object} config The config object
12457  */
12458
12459 Roo.bootstrap.CheckBox = function(config){
12460     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12461    
12462         this.addEvents({
12463             /**
12464             * @event check
12465             * Fires when the element is checked or unchecked.
12466             * @param {Roo.bootstrap.CheckBox} this This input
12467             * @param {Boolean} checked The new checked value
12468             */
12469            check : true
12470         });
12471 };
12472
12473 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12474     
12475     inputType: 'checkbox',
12476     inputValue: 1,
12477     valueOff: 0,
12478     boxLabel: false,
12479     checked: false,
12480     
12481     getAutoCreate : function()
12482     {
12483         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12484         
12485         var id = Roo.id();
12486         
12487         var cfg = {};
12488         
12489         cfg.cls = 'form-group' //input-group
12490         
12491         var input =  {
12492             tag: 'input',
12493             id : id,
12494             type : this.inputType,
12495             value : (!this.checked) ? this.valueOff : this.inputValue,
12496             cls : 'form-box',
12497             placeholder : this.placeholder || ''
12498             
12499         };
12500         
12501         if (this.disabled) {
12502             input.disabled=true;
12503         }
12504         
12505         if(this.checked){
12506             input.checked = this.checked;
12507         }
12508         
12509         if (this.name) {
12510             input.name = this.name;
12511         }
12512         
12513         if (this.size) {
12514             input.cls += ' input-' + this.size;
12515         }
12516         
12517         var settings=this;
12518         ['xs','sm','md','lg'].map(function(size){
12519             if (settings[size]) {
12520                 cfg.cls += ' col-' + size + '-' + settings[size];
12521             }
12522         });
12523         
12524         var inputblock = input;
12525         
12526         if (this.before || this.after) {
12527             
12528             inputblock = {
12529                 cls : 'input-group',
12530                 cn :  [] 
12531             };
12532             if (this.before) {
12533                 inputblock.cn.push({
12534                     tag :'span',
12535                     cls : 'input-group-addon',
12536                     html : this.before
12537                 });
12538             }
12539             inputblock.cn.push(input);
12540             if (this.after) {
12541                 inputblock.cn.push({
12542                     tag :'span',
12543                     cls : 'input-group-addon',
12544                     html : this.after
12545                 });
12546             }
12547             
12548         };
12549         
12550         if (align ==='left' && this.fieldLabel.length) {
12551                 Roo.log("left and has label");
12552                 cfg.cn = [
12553                     
12554                     {
12555                         tag: 'label',
12556                         'for' :  id,
12557                         cls : 'control-label col-md-' + this.labelWidth,
12558                         html : this.fieldLabel
12559                         
12560                     },
12561                     {
12562                         cls : "col-md-" + (12 - this.labelWidth), 
12563                         cn: [
12564                             inputblock
12565                         ]
12566                     }
12567                     
12568                 ];
12569         } else if ( this.fieldLabel.length) {
12570                 Roo.log(" label");
12571                 cfg.cn = [
12572                    
12573                     {
12574                         tag: this.boxLabel ? 'span' : 'label',
12575                         'for': id,
12576                         cls: 'control-label box-input-label',
12577                         //cls : 'input-group-addon',
12578                         html : this.fieldLabel
12579                         
12580                     },
12581                     
12582                     inputblock
12583                     
12584                 ];
12585
12586         } else {
12587             
12588                    Roo.log(" no label && no align");
12589                 cfg.cn = [
12590                     
12591                         inputblock
12592                     
12593                 ];
12594                 
12595                 
12596         };
12597         
12598         if(this.boxLabel){
12599             cfg.cn.push({
12600                 tag: 'label',
12601                 'for': id,
12602                 cls: 'box-label',
12603                 html: this.boxLabel
12604             })
12605         }
12606         
12607         return cfg;
12608         
12609     },
12610     
12611     /**
12612      * return the real input element.
12613      */
12614     inputEl: function ()
12615     {
12616         return this.el.select('input.form-box',true).first();
12617     },
12618     
12619     initEvents : function()
12620     {
12621 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12622         
12623         this.inputEl().on('click', this.onClick,  this);
12624         
12625     },
12626     
12627     onClick : function()
12628     {   
12629         this.setChecked(!this.checked);
12630     },
12631     
12632     setChecked : function(state,suppressEvent)
12633     {
12634         this.checked = state;
12635         
12636         this.inputEl().dom.checked = state;
12637         
12638         if(suppressEvent !== true){
12639             this.fireEvent('check', this, state);
12640         }
12641         
12642         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12643         
12644     },
12645     
12646     setValue : function(v,suppressEvent)
12647     {
12648         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12649     }
12650     
12651 });
12652
12653  
12654 /*
12655  * - LGPL
12656  *
12657  * Radio
12658  * 
12659  */
12660
12661 /**
12662  * @class Roo.bootstrap.Radio
12663  * @extends Roo.bootstrap.CheckBox
12664  * Bootstrap Radio class
12665
12666  * @constructor
12667  * Create a new Radio
12668  * @param {Object} config The config object
12669  */
12670
12671 Roo.bootstrap.Radio = function(config){
12672     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12673    
12674 };
12675
12676 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12677     
12678     inputType: 'radio',
12679     inputValue: '',
12680     valueOff: '',
12681     
12682     getAutoCreate : function()
12683     {
12684         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12685         
12686         var id = Roo.id();
12687         
12688         var cfg = {};
12689         
12690         cfg.cls = 'form-group' //input-group
12691         
12692         var input =  {
12693             tag: 'input',
12694             id : id,
12695             type : this.inputType,
12696             value : (!this.checked) ? this.valueOff : this.inputValue,
12697             cls : 'form-box',
12698             placeholder : this.placeholder || ''
12699             
12700         };
12701         
12702         if (this.disabled) {
12703             input.disabled=true;
12704         }
12705         
12706         if(this.checked){
12707             input.checked = this.checked;
12708         }
12709         
12710         if (this.name) {
12711             input.name = this.name;
12712         }
12713         
12714         if (this.size) {
12715             input.cls += ' input-' + this.size;
12716         }
12717         
12718         var settings=this;
12719         ['xs','sm','md','lg'].map(function(size){
12720             if (settings[size]) {
12721                 cfg.cls += ' col-' + size + '-' + settings[size];
12722             }
12723         });
12724         
12725         var inputblock = input;
12726         
12727         if (this.before || this.after) {
12728             
12729             inputblock = {
12730                 cls : 'input-group',
12731                 cn :  [] 
12732             };
12733             if (this.before) {
12734                 inputblock.cn.push({
12735                     tag :'span',
12736                     cls : 'input-group-addon',
12737                     html : this.before
12738                 });
12739             }
12740             inputblock.cn.push(input);
12741             if (this.after) {
12742                 inputblock.cn.push({
12743                     tag :'span',
12744                     cls : 'input-group-addon',
12745                     html : this.after
12746                 });
12747             }
12748             
12749         };
12750         
12751         if (align ==='left' && this.fieldLabel.length) {
12752                 Roo.log("left and has label");
12753                 cfg.cn = [
12754                     
12755                     {
12756                         tag: 'label',
12757                         'for' :  id,
12758                         cls : 'control-label col-md-' + this.labelWidth,
12759                         html : this.fieldLabel
12760                         
12761                     },
12762                     {
12763                         cls : "col-md-" + (12 - this.labelWidth), 
12764                         cn: [
12765                             inputblock
12766                         ]
12767                     }
12768                     
12769                 ];
12770         } else if ( this.fieldLabel.length) {
12771                 Roo.log(" label");
12772                  cfg.cn = [
12773                    
12774                     {
12775                         tag: 'label',
12776                         'for': id,
12777                         cls: 'control-label box-input-label',
12778                         //cls : 'input-group-addon',
12779                         html : this.fieldLabel
12780                         
12781                     },
12782                     
12783                     inputblock
12784                     
12785                 ];
12786
12787         } else {
12788             
12789                    Roo.log(" no label && no align");
12790                 cfg.cn = [
12791                     
12792                         inputblock
12793                     
12794                 ];
12795                 
12796                 
12797         };
12798         
12799         if(this.boxLabel){
12800             cfg.cn.push({
12801                 tag: 'label',
12802                 'for': id,
12803                 cls: 'box-label',
12804                 html: this.boxLabel
12805             })
12806         }
12807         
12808         return cfg;
12809         
12810     },
12811    
12812     onClick : function()
12813     {   
12814         this.setChecked(true);
12815     },
12816     
12817     setChecked : function(state,suppressEvent)
12818     {
12819         if(state){
12820             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12821                 v.dom.checked = false;
12822             });
12823         }
12824         
12825         this.checked = state;
12826         this.inputEl().dom.checked = state;
12827         
12828         if(suppressEvent !== true){
12829             this.fireEvent('check', this, state);
12830         }
12831         
12832         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12833         
12834     },
12835     
12836     getGroupValue : function()
12837     {
12838         var value = ''
12839         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12840             if(v.dom.checked == true){
12841                 value = v.dom.value;
12842             }
12843         });
12844         
12845         return value;
12846     },
12847     
12848     /**
12849      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12850      * @return {Mixed} value The field value
12851      */
12852     getValue : function(){
12853         return this.getGroupValue();
12854     }
12855     
12856 });
12857
12858  
12859 //<script type="text/javascript">
12860
12861 /*
12862  * Based  Ext JS Library 1.1.1
12863  * Copyright(c) 2006-2007, Ext JS, LLC.
12864  * LGPL
12865  *
12866  */
12867  
12868 /**
12869  * @class Roo.HtmlEditorCore
12870  * @extends Roo.Component
12871  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12872  *
12873  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12874  */
12875
12876 Roo.HtmlEditorCore = function(config){
12877     
12878     
12879     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12880     this.addEvents({
12881         /**
12882          * @event initialize
12883          * Fires when the editor is fully initialized (including the iframe)
12884          * @param {Roo.HtmlEditorCore} this
12885          */
12886         initialize: true,
12887         /**
12888          * @event activate
12889          * Fires when the editor is first receives the focus. Any insertion must wait
12890          * until after this event.
12891          * @param {Roo.HtmlEditorCore} this
12892          */
12893         activate: true,
12894          /**
12895          * @event beforesync
12896          * Fires before the textarea is updated with content from the editor iframe. Return false
12897          * to cancel the sync.
12898          * @param {Roo.HtmlEditorCore} this
12899          * @param {String} html
12900          */
12901         beforesync: true,
12902          /**
12903          * @event beforepush
12904          * Fires before the iframe editor is updated with content from the textarea. Return false
12905          * to cancel the push.
12906          * @param {Roo.HtmlEditorCore} this
12907          * @param {String} html
12908          */
12909         beforepush: true,
12910          /**
12911          * @event sync
12912          * Fires when the textarea is updated with content from the editor iframe.
12913          * @param {Roo.HtmlEditorCore} this
12914          * @param {String} html
12915          */
12916         sync: true,
12917          /**
12918          * @event push
12919          * Fires when the iframe editor is updated with content from the textarea.
12920          * @param {Roo.HtmlEditorCore} this
12921          * @param {String} html
12922          */
12923         push: true,
12924         
12925         /**
12926          * @event editorevent
12927          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12928          * @param {Roo.HtmlEditorCore} this
12929          */
12930         editorevent: true
12931     });
12932      
12933 };
12934
12935
12936 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12937
12938
12939      /**
12940      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12941      */
12942     
12943     owner : false,
12944     
12945      /**
12946      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12947      *                        Roo.resizable.
12948      */
12949     resizable : false,
12950      /**
12951      * @cfg {Number} height (in pixels)
12952      */   
12953     height: 300,
12954    /**
12955      * @cfg {Number} width (in pixels)
12956      */   
12957     width: 500,
12958     
12959     /**
12960      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12961      * 
12962      */
12963     stylesheets: false,
12964     
12965     // id of frame..
12966     frameId: false,
12967     
12968     // private properties
12969     validationEvent : false,
12970     deferHeight: true,
12971     initialized : false,
12972     activated : false,
12973     sourceEditMode : false,
12974     onFocus : Roo.emptyFn,
12975     iframePad:3,
12976     hideMode:'offsets',
12977     
12978     clearUp: true,
12979     
12980      
12981     
12982
12983     /**
12984      * Protected method that will not generally be called directly. It
12985      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12986      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12987      */
12988     getDocMarkup : function(){
12989         // body styles..
12990         var st = '';
12991         Roo.log(this.stylesheets);
12992         
12993         // inherit styels from page...?? 
12994         if (this.stylesheets === false) {
12995             
12996             Roo.get(document.head).select('style').each(function(node) {
12997                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12998             });
12999             
13000             Roo.get(document.head).select('link').each(function(node) { 
13001                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13002             });
13003             
13004         } else if (!this.stylesheets.length) {
13005                 // simple..
13006                 st = '<style type="text/css">' +
13007                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13008                    '</style>';
13009         } else {
13010             Roo.each(this.stylesheets, function(s) {
13011                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13012             });
13013             
13014         }
13015         
13016         st +=  '<style type="text/css">' +
13017             'IMG { cursor: pointer } ' +
13018         '</style>';
13019
13020         
13021         return '<html><head>' + st  +
13022             //<style type="text/css">' +
13023             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13024             //'</style>' +
13025             ' </head><body class="roo-htmleditor-body"></body></html>';
13026     },
13027
13028     // private
13029     onRender : function(ct, position)
13030     {
13031         var _t = this;
13032         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13033         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13034         
13035         
13036         this.el.dom.style.border = '0 none';
13037         this.el.dom.setAttribute('tabIndex', -1);
13038         this.el.addClass('x-hidden hide');
13039         
13040         
13041         
13042         if(Roo.isIE){ // fix IE 1px bogus margin
13043             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13044         }
13045        
13046         
13047         this.frameId = Roo.id();
13048         
13049          
13050         
13051         var iframe = this.owner.wrap.createChild({
13052             tag: 'iframe',
13053             cls: 'form-control', // bootstrap..
13054             id: this.frameId,
13055             name: this.frameId,
13056             frameBorder : 'no',
13057             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13058         }, this.el
13059         );
13060         
13061         
13062         this.iframe = iframe.dom;
13063
13064          this.assignDocWin();
13065         
13066         this.doc.designMode = 'on';
13067        
13068         this.doc.open();
13069         this.doc.write(this.getDocMarkup());
13070         this.doc.close();
13071
13072         
13073         var task = { // must defer to wait for browser to be ready
13074             run : function(){
13075                 //console.log("run task?" + this.doc.readyState);
13076                 this.assignDocWin();
13077                 if(this.doc.body || this.doc.readyState == 'complete'){
13078                     try {
13079                         this.doc.designMode="on";
13080                     } catch (e) {
13081                         return;
13082                     }
13083                     Roo.TaskMgr.stop(task);
13084                     this.initEditor.defer(10, this);
13085                 }
13086             },
13087             interval : 10,
13088             duration: 10000,
13089             scope: this
13090         };
13091         Roo.TaskMgr.start(task);
13092
13093         
13094          
13095     },
13096
13097     // private
13098     onResize : function(w, h)
13099     {
13100          Roo.log('resize: ' +w + ',' + h );
13101         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13102         if(!this.iframe){
13103             return;
13104         }
13105         if(typeof w == 'number'){
13106             
13107             this.iframe.style.width = w + 'px';
13108         }
13109         if(typeof h == 'number'){
13110             
13111             this.iframe.style.height = h + 'px';
13112             if(this.doc){
13113                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13114             }
13115         }
13116         
13117     },
13118
13119     /**
13120      * Toggles the editor between standard and source edit mode.
13121      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13122      */
13123     toggleSourceEdit : function(sourceEditMode){
13124         
13125         this.sourceEditMode = sourceEditMode === true;
13126         
13127         if(this.sourceEditMode){
13128  
13129             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13130             
13131         }else{
13132             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13133             //this.iframe.className = '';
13134             this.deferFocus();
13135         }
13136         //this.setSize(this.owner.wrap.getSize());
13137         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13138     },
13139
13140     
13141   
13142
13143     /**
13144      * Protected method that will not generally be called directly. If you need/want
13145      * custom HTML cleanup, this is the method you should override.
13146      * @param {String} html The HTML to be cleaned
13147      * return {String} The cleaned HTML
13148      */
13149     cleanHtml : function(html){
13150         html = String(html);
13151         if(html.length > 5){
13152             if(Roo.isSafari){ // strip safari nonsense
13153                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13154             }
13155         }
13156         if(html == '&nbsp;'){
13157             html = '';
13158         }
13159         return html;
13160     },
13161
13162     /**
13163      * HTML Editor -> Textarea
13164      * Protected method that will not generally be called directly. Syncs the contents
13165      * of the editor iframe with the textarea.
13166      */
13167     syncValue : function(){
13168         if(this.initialized){
13169             var bd = (this.doc.body || this.doc.documentElement);
13170             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13171             var html = bd.innerHTML;
13172             if(Roo.isSafari){
13173                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13174                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13175                 if(m && m[1]){
13176                     html = '<div style="'+m[0]+'">' + html + '</div>';
13177                 }
13178             }
13179             html = this.cleanHtml(html);
13180             // fix up the special chars.. normaly like back quotes in word...
13181             // however we do not want to do this with chinese..
13182             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13183                 var cc = b.charCodeAt();
13184                 if (
13185                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13186                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13187                     (cc >= 0xf900 && cc < 0xfb00 )
13188                 ) {
13189                         return b;
13190                 }
13191                 return "&#"+cc+";" 
13192             });
13193             if(this.owner.fireEvent('beforesync', this, html) !== false){
13194                 this.el.dom.value = html;
13195                 this.owner.fireEvent('sync', this, html);
13196             }
13197         }
13198     },
13199
13200     /**
13201      * Protected method that will not generally be called directly. Pushes the value of the textarea
13202      * into the iframe editor.
13203      */
13204     pushValue : function(){
13205         if(this.initialized){
13206             var v = this.el.dom.value.trim();
13207             
13208 //            if(v.length < 1){
13209 //                v = '&#160;';
13210 //            }
13211             
13212             if(this.owner.fireEvent('beforepush', this, v) !== false){
13213                 var d = (this.doc.body || this.doc.documentElement);
13214                 d.innerHTML = v;
13215                 this.cleanUpPaste();
13216                 this.el.dom.value = d.innerHTML;
13217                 this.owner.fireEvent('push', this, v);
13218             }
13219         }
13220     },
13221
13222     // private
13223     deferFocus : function(){
13224         this.focus.defer(10, this);
13225     },
13226
13227     // doc'ed in Field
13228     focus : function(){
13229         if(this.win && !this.sourceEditMode){
13230             this.win.focus();
13231         }else{
13232             this.el.focus();
13233         }
13234     },
13235     
13236     assignDocWin: function()
13237     {
13238         var iframe = this.iframe;
13239         
13240          if(Roo.isIE){
13241             this.doc = iframe.contentWindow.document;
13242             this.win = iframe.contentWindow;
13243         } else {
13244             if (!Roo.get(this.frameId)) {
13245                 return;
13246             }
13247             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13248             this.win = Roo.get(this.frameId).dom.contentWindow;
13249         }
13250     },
13251     
13252     // private
13253     initEditor : function(){
13254         //console.log("INIT EDITOR");
13255         this.assignDocWin();
13256         
13257         
13258         
13259         this.doc.designMode="on";
13260         this.doc.open();
13261         this.doc.write(this.getDocMarkup());
13262         this.doc.close();
13263         
13264         var dbody = (this.doc.body || this.doc.documentElement);
13265         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13266         // this copies styles from the containing element into thsi one..
13267         // not sure why we need all of this..
13268         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13269         ss['background-attachment'] = 'fixed'; // w3c
13270         dbody.bgProperties = 'fixed'; // ie
13271         Roo.DomHelper.applyStyles(dbody, ss);
13272         Roo.EventManager.on(this.doc, {
13273             //'mousedown': this.onEditorEvent,
13274             'mouseup': this.onEditorEvent,
13275             'dblclick': this.onEditorEvent,
13276             'click': this.onEditorEvent,
13277             'keyup': this.onEditorEvent,
13278             buffer:100,
13279             scope: this
13280         });
13281         if(Roo.isGecko){
13282             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13283         }
13284         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13285             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13286         }
13287         this.initialized = true;
13288
13289         this.owner.fireEvent('initialize', this);
13290         this.pushValue();
13291     },
13292
13293     // private
13294     onDestroy : function(){
13295         
13296         
13297         
13298         if(this.rendered){
13299             
13300             //for (var i =0; i < this.toolbars.length;i++) {
13301             //    // fixme - ask toolbars for heights?
13302             //    this.toolbars[i].onDestroy();
13303            // }
13304             
13305             //this.wrap.dom.innerHTML = '';
13306             //this.wrap.remove();
13307         }
13308     },
13309
13310     // private
13311     onFirstFocus : function(){
13312         
13313         this.assignDocWin();
13314         
13315         
13316         this.activated = true;
13317          
13318     
13319         if(Roo.isGecko){ // prevent silly gecko errors
13320             this.win.focus();
13321             var s = this.win.getSelection();
13322             if(!s.focusNode || s.focusNode.nodeType != 3){
13323                 var r = s.getRangeAt(0);
13324                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13325                 r.collapse(true);
13326                 this.deferFocus();
13327             }
13328             try{
13329                 this.execCmd('useCSS', true);
13330                 this.execCmd('styleWithCSS', false);
13331             }catch(e){}
13332         }
13333         this.owner.fireEvent('activate', this);
13334     },
13335
13336     // private
13337     adjustFont: function(btn){
13338         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13339         //if(Roo.isSafari){ // safari
13340         //    adjust *= 2;
13341        // }
13342         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13343         if(Roo.isSafari){ // safari
13344             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13345             v =  (v < 10) ? 10 : v;
13346             v =  (v > 48) ? 48 : v;
13347             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13348             
13349         }
13350         
13351         
13352         v = Math.max(1, v+adjust);
13353         
13354         this.execCmd('FontSize', v  );
13355     },
13356
13357     onEditorEvent : function(e){
13358         this.owner.fireEvent('editorevent', this, e);
13359       //  this.updateToolbar();
13360         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13361     },
13362
13363     insertTag : function(tg)
13364     {
13365         // could be a bit smarter... -> wrap the current selected tRoo..
13366         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13367             
13368             range = this.createRange(this.getSelection());
13369             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13370             wrappingNode.appendChild(range.extractContents());
13371             range.insertNode(wrappingNode);
13372
13373             return;
13374             
13375             
13376             
13377         }
13378         this.execCmd("formatblock",   tg);
13379         
13380     },
13381     
13382     insertText : function(txt)
13383     {
13384         
13385         
13386         var range = this.createRange();
13387         range.deleteContents();
13388                //alert(Sender.getAttribute('label'));
13389                
13390         range.insertNode(this.doc.createTextNode(txt));
13391     } ,
13392     
13393      
13394
13395     /**
13396      * Executes a Midas editor command on the editor document and performs necessary focus and
13397      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13398      * @param {String} cmd The Midas command
13399      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13400      */
13401     relayCmd : function(cmd, value){
13402         this.win.focus();
13403         this.execCmd(cmd, value);
13404         this.owner.fireEvent('editorevent', this);
13405         //this.updateToolbar();
13406         this.owner.deferFocus();
13407     },
13408
13409     /**
13410      * Executes a Midas editor command directly on the editor document.
13411      * For visual commands, you should use {@link #relayCmd} instead.
13412      * <b>This should only be called after the editor is initialized.</b>
13413      * @param {String} cmd The Midas command
13414      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13415      */
13416     execCmd : function(cmd, value){
13417         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13418         this.syncValue();
13419     },
13420  
13421  
13422    
13423     /**
13424      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13425      * to insert tRoo.
13426      * @param {String} text | dom node.. 
13427      */
13428     insertAtCursor : function(text)
13429     {
13430         
13431         
13432         
13433         if(!this.activated){
13434             return;
13435         }
13436         /*
13437         if(Roo.isIE){
13438             this.win.focus();
13439             var r = this.doc.selection.createRange();
13440             if(r){
13441                 r.collapse(true);
13442                 r.pasteHTML(text);
13443                 this.syncValue();
13444                 this.deferFocus();
13445             
13446             }
13447             return;
13448         }
13449         */
13450         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13451             this.win.focus();
13452             
13453             
13454             // from jquery ui (MIT licenced)
13455             var range, node;
13456             var win = this.win;
13457             
13458             if (win.getSelection && win.getSelection().getRangeAt) {
13459                 range = win.getSelection().getRangeAt(0);
13460                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13461                 range.insertNode(node);
13462             } else if (win.document.selection && win.document.selection.createRange) {
13463                 // no firefox support
13464                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13465                 win.document.selection.createRange().pasteHTML(txt);
13466             } else {
13467                 // no firefox support
13468                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13469                 this.execCmd('InsertHTML', txt);
13470             } 
13471             
13472             this.syncValue();
13473             
13474             this.deferFocus();
13475         }
13476     },
13477  // private
13478     mozKeyPress : function(e){
13479         if(e.ctrlKey){
13480             var c = e.getCharCode(), cmd;
13481           
13482             if(c > 0){
13483                 c = String.fromCharCode(c).toLowerCase();
13484                 switch(c){
13485                     case 'b':
13486                         cmd = 'bold';
13487                         break;
13488                     case 'i':
13489                         cmd = 'italic';
13490                         break;
13491                     
13492                     case 'u':
13493                         cmd = 'underline';
13494                         break;
13495                     
13496                     case 'v':
13497                         this.cleanUpPaste.defer(100, this);
13498                         return;
13499                         
13500                 }
13501                 if(cmd){
13502                     this.win.focus();
13503                     this.execCmd(cmd);
13504                     this.deferFocus();
13505                     e.preventDefault();
13506                 }
13507                 
13508             }
13509         }
13510     },
13511
13512     // private
13513     fixKeys : function(){ // load time branching for fastest keydown performance
13514         if(Roo.isIE){
13515             return function(e){
13516                 var k = e.getKey(), r;
13517                 if(k == e.TAB){
13518                     e.stopEvent();
13519                     r = this.doc.selection.createRange();
13520                     if(r){
13521                         r.collapse(true);
13522                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13523                         this.deferFocus();
13524                     }
13525                     return;
13526                 }
13527                 
13528                 if(k == e.ENTER){
13529                     r = this.doc.selection.createRange();
13530                     if(r){
13531                         var target = r.parentElement();
13532                         if(!target || target.tagName.toLowerCase() != 'li'){
13533                             e.stopEvent();
13534                             r.pasteHTML('<br />');
13535                             r.collapse(false);
13536                             r.select();
13537                         }
13538                     }
13539                 }
13540                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13541                     this.cleanUpPaste.defer(100, this);
13542                     return;
13543                 }
13544                 
13545                 
13546             };
13547         }else if(Roo.isOpera){
13548             return function(e){
13549                 var k = e.getKey();
13550                 if(k == e.TAB){
13551                     e.stopEvent();
13552                     this.win.focus();
13553                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13554                     this.deferFocus();
13555                 }
13556                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13557                     this.cleanUpPaste.defer(100, this);
13558                     return;
13559                 }
13560                 
13561             };
13562         }else if(Roo.isSafari){
13563             return function(e){
13564                 var k = e.getKey();
13565                 
13566                 if(k == e.TAB){
13567                     e.stopEvent();
13568                     this.execCmd('InsertText','\t');
13569                     this.deferFocus();
13570                     return;
13571                 }
13572                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13573                     this.cleanUpPaste.defer(100, this);
13574                     return;
13575                 }
13576                 
13577              };
13578         }
13579     }(),
13580     
13581     getAllAncestors: function()
13582     {
13583         var p = this.getSelectedNode();
13584         var a = [];
13585         if (!p) {
13586             a.push(p); // push blank onto stack..
13587             p = this.getParentElement();
13588         }
13589         
13590         
13591         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13592             a.push(p);
13593             p = p.parentNode;
13594         }
13595         a.push(this.doc.body);
13596         return a;
13597     },
13598     lastSel : false,
13599     lastSelNode : false,
13600     
13601     
13602     getSelection : function() 
13603     {
13604         this.assignDocWin();
13605         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13606     },
13607     
13608     getSelectedNode: function() 
13609     {
13610         // this may only work on Gecko!!!
13611         
13612         // should we cache this!!!!
13613         
13614         
13615         
13616          
13617         var range = this.createRange(this.getSelection()).cloneRange();
13618         
13619         if (Roo.isIE) {
13620             var parent = range.parentElement();
13621             while (true) {
13622                 var testRange = range.duplicate();
13623                 testRange.moveToElementText(parent);
13624                 if (testRange.inRange(range)) {
13625                     break;
13626                 }
13627                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13628                     break;
13629                 }
13630                 parent = parent.parentElement;
13631             }
13632             return parent;
13633         }
13634         
13635         // is ancestor a text element.
13636         var ac =  range.commonAncestorContainer;
13637         if (ac.nodeType == 3) {
13638             ac = ac.parentNode;
13639         }
13640         
13641         var ar = ac.childNodes;
13642          
13643         var nodes = [];
13644         var other_nodes = [];
13645         var has_other_nodes = false;
13646         for (var i=0;i<ar.length;i++) {
13647             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13648                 continue;
13649             }
13650             // fullly contained node.
13651             
13652             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13653                 nodes.push(ar[i]);
13654                 continue;
13655             }
13656             
13657             // probably selected..
13658             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13659                 other_nodes.push(ar[i]);
13660                 continue;
13661             }
13662             // outer..
13663             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13664                 continue;
13665             }
13666             
13667             
13668             has_other_nodes = true;
13669         }
13670         if (!nodes.length && other_nodes.length) {
13671             nodes= other_nodes;
13672         }
13673         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13674             return false;
13675         }
13676         
13677         return nodes[0];
13678     },
13679     createRange: function(sel)
13680     {
13681         // this has strange effects when using with 
13682         // top toolbar - not sure if it's a great idea.
13683         //this.editor.contentWindow.focus();
13684         if (typeof sel != "undefined") {
13685             try {
13686                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13687             } catch(e) {
13688                 return this.doc.createRange();
13689             }
13690         } else {
13691             return this.doc.createRange();
13692         }
13693     },
13694     getParentElement: function()
13695     {
13696         
13697         this.assignDocWin();
13698         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13699         
13700         var range = this.createRange(sel);
13701          
13702         try {
13703             var p = range.commonAncestorContainer;
13704             while (p.nodeType == 3) { // text node
13705                 p = p.parentNode;
13706             }
13707             return p;
13708         } catch (e) {
13709             return null;
13710         }
13711     
13712     },
13713     /***
13714      *
13715      * Range intersection.. the hard stuff...
13716      *  '-1' = before
13717      *  '0' = hits..
13718      *  '1' = after.
13719      *         [ -- selected range --- ]
13720      *   [fail]                        [fail]
13721      *
13722      *    basically..
13723      *      if end is before start or  hits it. fail.
13724      *      if start is after end or hits it fail.
13725      *
13726      *   if either hits (but other is outside. - then it's not 
13727      *   
13728      *    
13729      **/
13730     
13731     
13732     // @see http://www.thismuchiknow.co.uk/?p=64.
13733     rangeIntersectsNode : function(range, node)
13734     {
13735         var nodeRange = node.ownerDocument.createRange();
13736         try {
13737             nodeRange.selectNode(node);
13738         } catch (e) {
13739             nodeRange.selectNodeContents(node);
13740         }
13741     
13742         var rangeStartRange = range.cloneRange();
13743         rangeStartRange.collapse(true);
13744     
13745         var rangeEndRange = range.cloneRange();
13746         rangeEndRange.collapse(false);
13747     
13748         var nodeStartRange = nodeRange.cloneRange();
13749         nodeStartRange.collapse(true);
13750     
13751         var nodeEndRange = nodeRange.cloneRange();
13752         nodeEndRange.collapse(false);
13753     
13754         return rangeStartRange.compareBoundaryPoints(
13755                  Range.START_TO_START, nodeEndRange) == -1 &&
13756                rangeEndRange.compareBoundaryPoints(
13757                  Range.START_TO_START, nodeStartRange) == 1;
13758         
13759          
13760     },
13761     rangeCompareNode : function(range, node)
13762     {
13763         var nodeRange = node.ownerDocument.createRange();
13764         try {
13765             nodeRange.selectNode(node);
13766         } catch (e) {
13767             nodeRange.selectNodeContents(node);
13768         }
13769         
13770         
13771         range.collapse(true);
13772     
13773         nodeRange.collapse(true);
13774      
13775         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13776         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13777          
13778         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13779         
13780         var nodeIsBefore   =  ss == 1;
13781         var nodeIsAfter    = ee == -1;
13782         
13783         if (nodeIsBefore && nodeIsAfter)
13784             return 0; // outer
13785         if (!nodeIsBefore && nodeIsAfter)
13786             return 1; //right trailed.
13787         
13788         if (nodeIsBefore && !nodeIsAfter)
13789             return 2;  // left trailed.
13790         // fully contined.
13791         return 3;
13792     },
13793
13794     // private? - in a new class?
13795     cleanUpPaste :  function()
13796     {
13797         // cleans up the whole document..
13798         Roo.log('cleanuppaste');
13799         
13800         this.cleanUpChildren(this.doc.body);
13801         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13802         if (clean != this.doc.body.innerHTML) {
13803             this.doc.body.innerHTML = clean;
13804         }
13805         
13806     },
13807     
13808     cleanWordChars : function(input) {// change the chars to hex code
13809         var he = Roo.HtmlEditorCore;
13810         
13811         var output = input;
13812         Roo.each(he.swapCodes, function(sw) { 
13813             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13814             
13815             output = output.replace(swapper, sw[1]);
13816         });
13817         
13818         return output;
13819     },
13820     
13821     
13822     cleanUpChildren : function (n)
13823     {
13824         if (!n.childNodes.length) {
13825             return;
13826         }
13827         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13828            this.cleanUpChild(n.childNodes[i]);
13829         }
13830     },
13831     
13832     
13833         
13834     
13835     cleanUpChild : function (node)
13836     {
13837         var ed = this;
13838         //console.log(node);
13839         if (node.nodeName == "#text") {
13840             // clean up silly Windows -- stuff?
13841             return; 
13842         }
13843         if (node.nodeName == "#comment") {
13844             node.parentNode.removeChild(node);
13845             // clean up silly Windows -- stuff?
13846             return; 
13847         }
13848         
13849         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13850             // remove node.
13851             node.parentNode.removeChild(node);
13852             return;
13853             
13854         }
13855         
13856         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13857         
13858         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13859         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13860         
13861         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13862         //    remove_keep_children = true;
13863         //}
13864         
13865         if (remove_keep_children) {
13866             this.cleanUpChildren(node);
13867             // inserts everything just before this node...
13868             while (node.childNodes.length) {
13869                 var cn = node.childNodes[0];
13870                 node.removeChild(cn);
13871                 node.parentNode.insertBefore(cn, node);
13872             }
13873             node.parentNode.removeChild(node);
13874             return;
13875         }
13876         
13877         if (!node.attributes || !node.attributes.length) {
13878             this.cleanUpChildren(node);
13879             return;
13880         }
13881         
13882         function cleanAttr(n,v)
13883         {
13884             
13885             if (v.match(/^\./) || v.match(/^\//)) {
13886                 return;
13887             }
13888             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13889                 return;
13890             }
13891             if (v.match(/^#/)) {
13892                 return;
13893             }
13894 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13895             node.removeAttribute(n);
13896             
13897         }
13898         
13899         function cleanStyle(n,v)
13900         {
13901             if (v.match(/expression/)) { //XSS?? should we even bother..
13902                 node.removeAttribute(n);
13903                 return;
13904             }
13905             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13906             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13907             
13908             
13909             var parts = v.split(/;/);
13910             var clean = [];
13911             
13912             Roo.each(parts, function(p) {
13913                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13914                 if (!p.length) {
13915                     return true;
13916                 }
13917                 var l = p.split(':').shift().replace(/\s+/g,'');
13918                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13919                 
13920                 if ( cblack.indexOf(l) > -1) {
13921 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13922                     //node.removeAttribute(n);
13923                     return true;
13924                 }
13925                 //Roo.log()
13926                 // only allow 'c whitelisted system attributes'
13927                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13928 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13929                     //node.removeAttribute(n);
13930                     return true;
13931                 }
13932                 
13933                 
13934                  
13935                 
13936                 clean.push(p);
13937                 return true;
13938             });
13939             if (clean.length) { 
13940                 node.setAttribute(n, clean.join(';'));
13941             } else {
13942                 node.removeAttribute(n);
13943             }
13944             
13945         }
13946         
13947         
13948         for (var i = node.attributes.length-1; i > -1 ; i--) {
13949             var a = node.attributes[i];
13950             //console.log(a);
13951             
13952             if (a.name.toLowerCase().substr(0,2)=='on')  {
13953                 node.removeAttribute(a.name);
13954                 continue;
13955             }
13956             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13957                 node.removeAttribute(a.name);
13958                 continue;
13959             }
13960             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13961                 cleanAttr(a.name,a.value); // fixme..
13962                 continue;
13963             }
13964             if (a.name == 'style') {
13965                 cleanStyle(a.name,a.value);
13966                 continue;
13967             }
13968             /// clean up MS crap..
13969             // tecnically this should be a list of valid class'es..
13970             
13971             
13972             if (a.name == 'class') {
13973                 if (a.value.match(/^Mso/)) {
13974                     node.className = '';
13975                 }
13976                 
13977                 if (a.value.match(/body/)) {
13978                     node.className = '';
13979                 }
13980                 continue;
13981             }
13982             
13983             // style cleanup!?
13984             // class cleanup?
13985             
13986         }
13987         
13988         
13989         this.cleanUpChildren(node);
13990         
13991         
13992     }
13993     
13994     
13995     // hide stuff that is not compatible
13996     /**
13997      * @event blur
13998      * @hide
13999      */
14000     /**
14001      * @event change
14002      * @hide
14003      */
14004     /**
14005      * @event focus
14006      * @hide
14007      */
14008     /**
14009      * @event specialkey
14010      * @hide
14011      */
14012     /**
14013      * @cfg {String} fieldClass @hide
14014      */
14015     /**
14016      * @cfg {String} focusClass @hide
14017      */
14018     /**
14019      * @cfg {String} autoCreate @hide
14020      */
14021     /**
14022      * @cfg {String} inputType @hide
14023      */
14024     /**
14025      * @cfg {String} invalidClass @hide
14026      */
14027     /**
14028      * @cfg {String} invalidText @hide
14029      */
14030     /**
14031      * @cfg {String} msgFx @hide
14032      */
14033     /**
14034      * @cfg {String} validateOnBlur @hide
14035      */
14036 });
14037
14038 Roo.HtmlEditorCore.white = [
14039         'area', 'br', 'img', 'input', 'hr', 'wbr',
14040         
14041        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14042        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14043        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14044        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14045        'table',   'ul',         'xmp', 
14046        
14047        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14048       'thead',   'tr', 
14049      
14050       'dir', 'menu', 'ol', 'ul', 'dl',
14051        
14052       'embed',  'object'
14053 ];
14054
14055
14056 Roo.HtmlEditorCore.black = [
14057     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14058         'applet', // 
14059         'base',   'basefont', 'bgsound', 'blink',  'body', 
14060         'frame',  'frameset', 'head',    'html',   'ilayer', 
14061         'iframe', 'layer',  'link',     'meta',    'object',   
14062         'script', 'style' ,'title',  'xml' // clean later..
14063 ];
14064 Roo.HtmlEditorCore.clean = [
14065     'script', 'style', 'title', 'xml'
14066 ];
14067 Roo.HtmlEditorCore.remove = [
14068     'font'
14069 ];
14070 // attributes..
14071
14072 Roo.HtmlEditorCore.ablack = [
14073     'on'
14074 ];
14075     
14076 Roo.HtmlEditorCore.aclean = [ 
14077     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14078 ];
14079
14080 // protocols..
14081 Roo.HtmlEditorCore.pwhite= [
14082         'http',  'https',  'mailto'
14083 ];
14084
14085 // white listed style attributes.
14086 Roo.HtmlEditorCore.cwhite= [
14087       //  'text-align', /// default is to allow most things..
14088       
14089          
14090 //        'font-size'//??
14091 ];
14092
14093 // black listed style attributes.
14094 Roo.HtmlEditorCore.cblack= [
14095       //  'font-size' -- this can be set by the project 
14096 ];
14097
14098
14099 Roo.HtmlEditorCore.swapCodes   =[ 
14100     [    8211, "--" ], 
14101     [    8212, "--" ], 
14102     [    8216,  "'" ],  
14103     [    8217, "'" ],  
14104     [    8220, '"' ],  
14105     [    8221, '"' ],  
14106     [    8226, "*" ],  
14107     [    8230, "..." ]
14108 ]; 
14109
14110     /*
14111  * - LGPL
14112  *
14113  * HtmlEditor
14114  * 
14115  */
14116
14117 /**
14118  * @class Roo.bootstrap.HtmlEditor
14119  * @extends Roo.bootstrap.TextArea
14120  * Bootstrap HtmlEditor class
14121
14122  * @constructor
14123  * Create a new HtmlEditor
14124  * @param {Object} config The config object
14125  */
14126
14127 Roo.bootstrap.HtmlEditor = function(config){
14128     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14129     if (!this.toolbars) {
14130         this.toolbars = [];
14131     }
14132     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14133     this.addEvents({
14134             /**
14135              * @event initialize
14136              * Fires when the editor is fully initialized (including the iframe)
14137              * @param {HtmlEditor} this
14138              */
14139             initialize: true,
14140             /**
14141              * @event activate
14142              * Fires when the editor is first receives the focus. Any insertion must wait
14143              * until after this event.
14144              * @param {HtmlEditor} this
14145              */
14146             activate: true,
14147              /**
14148              * @event beforesync
14149              * Fires before the textarea is updated with content from the editor iframe. Return false
14150              * to cancel the sync.
14151              * @param {HtmlEditor} this
14152              * @param {String} html
14153              */
14154             beforesync: true,
14155              /**
14156              * @event beforepush
14157              * Fires before the iframe editor is updated with content from the textarea. Return false
14158              * to cancel the push.
14159              * @param {HtmlEditor} this
14160              * @param {String} html
14161              */
14162             beforepush: true,
14163              /**
14164              * @event sync
14165              * Fires when the textarea is updated with content from the editor iframe.
14166              * @param {HtmlEditor} this
14167              * @param {String} html
14168              */
14169             sync: true,
14170              /**
14171              * @event push
14172              * Fires when the iframe editor is updated with content from the textarea.
14173              * @param {HtmlEditor} this
14174              * @param {String} html
14175              */
14176             push: true,
14177              /**
14178              * @event editmodechange
14179              * Fires when the editor switches edit modes
14180              * @param {HtmlEditor} this
14181              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14182              */
14183             editmodechange: true,
14184             /**
14185              * @event editorevent
14186              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14187              * @param {HtmlEditor} this
14188              */
14189             editorevent: true,
14190             /**
14191              * @event firstfocus
14192              * Fires when on first focus - needed by toolbars..
14193              * @param {HtmlEditor} this
14194              */
14195             firstfocus: true,
14196             /**
14197              * @event autosave
14198              * Auto save the htmlEditor value as a file into Events
14199              * @param {HtmlEditor} this
14200              */
14201             autosave: true,
14202             /**
14203              * @event savedpreview
14204              * preview the saved version of htmlEditor
14205              * @param {HtmlEditor} this
14206              */
14207             savedpreview: true
14208         });
14209 };
14210
14211
14212 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14213     
14214     
14215       /**
14216      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14217      */
14218     toolbars : false,
14219    
14220      /**
14221      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14222      *                        Roo.resizable.
14223      */
14224     resizable : false,
14225      /**
14226      * @cfg {Number} height (in pixels)
14227      */   
14228     height: 300,
14229    /**
14230      * @cfg {Number} width (in pixels)
14231      */   
14232     width: false,
14233     
14234     /**
14235      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14236      * 
14237      */
14238     stylesheets: false,
14239     
14240     // id of frame..
14241     frameId: false,
14242     
14243     // private properties
14244     validationEvent : false,
14245     deferHeight: true,
14246     initialized : false,
14247     activated : false,
14248     
14249     onFocus : Roo.emptyFn,
14250     iframePad:3,
14251     hideMode:'offsets',
14252     
14253     
14254     tbContainer : false,
14255     
14256     toolbarContainer :function() {
14257         return this.wrap.select('.x-html-editor-tb',true).first();
14258     },
14259
14260     /**
14261      * Protected method that will not generally be called directly. It
14262      * is called when the editor creates its toolbar. Override this method if you need to
14263      * add custom toolbar buttons.
14264      * @param {HtmlEditor} editor
14265      */
14266     createToolbar : function(){
14267         
14268         Roo.log("create toolbars");
14269         
14270         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14271         this.toolbars[0].render(this.toolbarContainer());
14272         
14273         return;
14274         
14275 //        if (!editor.toolbars || !editor.toolbars.length) {
14276 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14277 //        }
14278 //        
14279 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14280 //            editor.toolbars[i] = Roo.factory(
14281 //                    typeof(editor.toolbars[i]) == 'string' ?
14282 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14283 //                Roo.bootstrap.HtmlEditor);
14284 //            editor.toolbars[i].init(editor);
14285 //        }
14286     },
14287
14288      
14289     // private
14290     onRender : function(ct, position)
14291     {
14292        // Roo.log("Call onRender: " + this.xtype);
14293         var _t = this;
14294         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14295       
14296         this.wrap = this.inputEl().wrap({
14297             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14298         });
14299         
14300         this.editorcore.onRender(ct, position);
14301          
14302         if (this.resizable) {
14303             this.resizeEl = new Roo.Resizable(this.wrap, {
14304                 pinned : true,
14305                 wrap: true,
14306                 dynamic : true,
14307                 minHeight : this.height,
14308                 height: this.height,
14309                 handles : this.resizable,
14310                 width: this.width,
14311                 listeners : {
14312                     resize : function(r, w, h) {
14313                         _t.onResize(w,h); // -something
14314                     }
14315                 }
14316             });
14317             
14318         }
14319         this.createToolbar(this);
14320        
14321         
14322         if(!this.width && this.resizable){
14323             this.setSize(this.wrap.getSize());
14324         }
14325         if (this.resizeEl) {
14326             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14327             // should trigger onReize..
14328         }
14329         
14330     },
14331
14332     // private
14333     onResize : function(w, h)
14334     {
14335         Roo.log('resize: ' +w + ',' + h );
14336         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14337         var ew = false;
14338         var eh = false;
14339         
14340         if(this.inputEl() ){
14341             if(typeof w == 'number'){
14342                 var aw = w - this.wrap.getFrameWidth('lr');
14343                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14344                 ew = aw;
14345             }
14346             if(typeof h == 'number'){
14347                  var tbh = -11;  // fixme it needs to tool bar size!
14348                 for (var i =0; i < this.toolbars.length;i++) {
14349                     // fixme - ask toolbars for heights?
14350                     tbh += this.toolbars[i].el.getHeight();
14351                     //if (this.toolbars[i].footer) {
14352                     //    tbh += this.toolbars[i].footer.el.getHeight();
14353                     //}
14354                 }
14355               
14356                 
14357                 
14358                 
14359                 
14360                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14361                 ah -= 5; // knock a few pixes off for look..
14362                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14363                 var eh = ah;
14364             }
14365         }
14366         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14367         this.editorcore.onResize(ew,eh);
14368         
14369     },
14370
14371     /**
14372      * Toggles the editor between standard and source edit mode.
14373      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14374      */
14375     toggleSourceEdit : function(sourceEditMode)
14376     {
14377         this.editorcore.toggleSourceEdit(sourceEditMode);
14378         
14379         if(this.editorcore.sourceEditMode){
14380             Roo.log('editor - showing textarea');
14381             
14382 //            Roo.log('in');
14383 //            Roo.log(this.syncValue());
14384             this.syncValue();
14385             this.inputEl().removeClass('hide');
14386             this.inputEl().dom.removeAttribute('tabIndex');
14387             this.inputEl().focus();
14388         }else{
14389             Roo.log('editor - hiding textarea');
14390 //            Roo.log('out')
14391 //            Roo.log(this.pushValue()); 
14392             this.pushValue();
14393             
14394             this.inputEl().addClass('hide');
14395             this.inputEl().dom.setAttribute('tabIndex', -1);
14396             //this.deferFocus();
14397         }
14398          
14399         if(this.resizable){
14400             this.setSize(this.wrap.getSize());
14401         }
14402         
14403         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14404     },
14405  
14406     // private (for BoxComponent)
14407     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14408
14409     // private (for BoxComponent)
14410     getResizeEl : function(){
14411         return this.wrap;
14412     },
14413
14414     // private (for BoxComponent)
14415     getPositionEl : function(){
14416         return this.wrap;
14417     },
14418
14419     // private
14420     initEvents : function(){
14421         this.originalValue = this.getValue();
14422     },
14423
14424 //    /**
14425 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14426 //     * @method
14427 //     */
14428 //    markInvalid : Roo.emptyFn,
14429 //    /**
14430 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14431 //     * @method
14432 //     */
14433 //    clearInvalid : Roo.emptyFn,
14434
14435     setValue : function(v){
14436         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14437         this.editorcore.pushValue();
14438     },
14439
14440      
14441     // private
14442     deferFocus : function(){
14443         this.focus.defer(10, this);
14444     },
14445
14446     // doc'ed in Field
14447     focus : function(){
14448         this.editorcore.focus();
14449         
14450     },
14451       
14452
14453     // private
14454     onDestroy : function(){
14455         
14456         
14457         
14458         if(this.rendered){
14459             
14460             for (var i =0; i < this.toolbars.length;i++) {
14461                 // fixme - ask toolbars for heights?
14462                 this.toolbars[i].onDestroy();
14463             }
14464             
14465             this.wrap.dom.innerHTML = '';
14466             this.wrap.remove();
14467         }
14468     },
14469
14470     // private
14471     onFirstFocus : function(){
14472         //Roo.log("onFirstFocus");
14473         this.editorcore.onFirstFocus();
14474          for (var i =0; i < this.toolbars.length;i++) {
14475             this.toolbars[i].onFirstFocus();
14476         }
14477         
14478     },
14479     
14480     // private
14481     syncValue : function()
14482     {   
14483         this.editorcore.syncValue();
14484     },
14485     
14486     pushValue : function()
14487     {   
14488         this.editorcore.pushValue();
14489     }
14490      
14491     
14492     // hide stuff that is not compatible
14493     /**
14494      * @event blur
14495      * @hide
14496      */
14497     /**
14498      * @event change
14499      * @hide
14500      */
14501     /**
14502      * @event focus
14503      * @hide
14504      */
14505     /**
14506      * @event specialkey
14507      * @hide
14508      */
14509     /**
14510      * @cfg {String} fieldClass @hide
14511      */
14512     /**
14513      * @cfg {String} focusClass @hide
14514      */
14515     /**
14516      * @cfg {String} autoCreate @hide
14517      */
14518     /**
14519      * @cfg {String} inputType @hide
14520      */
14521     /**
14522      * @cfg {String} invalidClass @hide
14523      */
14524     /**
14525      * @cfg {String} invalidText @hide
14526      */
14527     /**
14528      * @cfg {String} msgFx @hide
14529      */
14530     /**
14531      * @cfg {String} validateOnBlur @hide
14532      */
14533 });
14534  
14535     
14536    
14537    
14538    
14539       
14540
14541 /**
14542  * @class Roo.bootstrap.HtmlEditorToolbar1
14543  * Basic Toolbar
14544  * 
14545  * Usage:
14546  *
14547  new Roo.bootstrap.HtmlEditor({
14548     ....
14549     toolbars : [
14550         new Roo.bootstrap.HtmlEditorToolbar1({
14551             disable : { fonts: 1 , format: 1, ..., ... , ...],
14552             btns : [ .... ]
14553         })
14554     }
14555      
14556  * 
14557  * @cfg {Object} disable List of elements to disable..
14558  * @cfg {Array} btns List of additional buttons.
14559  * 
14560  * 
14561  * NEEDS Extra CSS? 
14562  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14563  */
14564  
14565 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14566 {
14567     
14568     Roo.apply(this, config);
14569     
14570     // default disabled, based on 'good practice'..
14571     this.disable = this.disable || {};
14572     Roo.applyIf(this.disable, {
14573         fontSize : true,
14574         colors : true,
14575         specialElements : true
14576     });
14577     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14578     
14579     this.editor = config.editor;
14580     this.editorcore = config.editor.editorcore;
14581     
14582     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14583     
14584     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14585     // dont call parent... till later.
14586 }
14587 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14588     
14589     
14590     bar : true,
14591     
14592     editor : false,
14593     editorcore : false,
14594     
14595     
14596     formats : [
14597         "p" ,  
14598         "h1","h2","h3","h4","h5","h6", 
14599         "pre", "code", 
14600         "abbr", "acronym", "address", "cite", "samp", "var",
14601         'div','span'
14602     ],
14603     
14604     onRender : function(ct, position)
14605     {
14606        // Roo.log("Call onRender: " + this.xtype);
14607         
14608        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14609        Roo.log(this.el);
14610        this.el.dom.style.marginBottom = '0';
14611        var _this = this;
14612        var editorcore = this.editorcore;
14613        var editor= this.editor;
14614        
14615        var children = [];
14616        var btn = function(id,cmd , toggle, handler){
14617        
14618             var  event = toggle ? 'toggle' : 'click';
14619        
14620             var a = {
14621                 size : 'sm',
14622                 xtype: 'Button',
14623                 xns: Roo.bootstrap,
14624                 glyphicon : id,
14625                 cmd : id || cmd,
14626                 enableToggle:toggle !== false,
14627                 //html : 'submit'
14628                 pressed : toggle ? false : null,
14629                 listeners : {}
14630             }
14631             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14632                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14633             }
14634             children.push(a);
14635             return a;
14636        }
14637         
14638         var style = {
14639                 xtype: 'Button',
14640                 size : 'sm',
14641                 xns: Roo.bootstrap,
14642                 glyphicon : 'font',
14643                 //html : 'submit'
14644                 menu : {
14645                     xtype: 'Menu',
14646                     xns: Roo.bootstrap,
14647                     items:  []
14648                 }
14649         };
14650         Roo.each(this.formats, function(f) {
14651             style.menu.items.push({
14652                 xtype :'MenuItem',
14653                 xns: Roo.bootstrap,
14654                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14655                 tagname : f,
14656                 listeners : {
14657                     click : function()
14658                     {
14659                         editorcore.insertTag(this.tagname);
14660                         editor.focus();
14661                     }
14662                 }
14663                 
14664             });
14665         });
14666          children.push(style);   
14667             
14668             
14669         btn('bold',false,true);
14670         btn('italic',false,true);
14671         btn('align-left', 'justifyleft',true);
14672         btn('align-center', 'justifycenter',true);
14673         btn('align-right' , 'justifyright',true);
14674         btn('link', false, false, function(btn) {
14675             //Roo.log("create link?");
14676             var url = prompt(this.createLinkText, this.defaultLinkValue);
14677             if(url && url != 'http:/'+'/'){
14678                 this.editorcore.relayCmd('createlink', url);
14679             }
14680         }),
14681         btn('list','insertunorderedlist',true);
14682         btn('pencil', false,true, function(btn){
14683                 Roo.log(this);
14684                 
14685                 this.toggleSourceEdit(btn.pressed);
14686         });
14687         /*
14688         var cog = {
14689                 xtype: 'Button',
14690                 size : 'sm',
14691                 xns: Roo.bootstrap,
14692                 glyphicon : 'cog',
14693                 //html : 'submit'
14694                 menu : {
14695                     xtype: 'Menu',
14696                     xns: Roo.bootstrap,
14697                     items:  []
14698                 }
14699         };
14700         
14701         cog.menu.items.push({
14702             xtype :'MenuItem',
14703             xns: Roo.bootstrap,
14704             html : Clean styles,
14705             tagname : f,
14706             listeners : {
14707                 click : function()
14708                 {
14709                     editorcore.insertTag(this.tagname);
14710                     editor.focus();
14711                 }
14712             }
14713             
14714         });
14715        */
14716         
14717          
14718        this.xtype = 'Navbar';
14719         
14720         for(var i=0;i< children.length;i++) {
14721             
14722             this.buttons.add(this.addxtypeChild(children[i]));
14723             
14724         }
14725         
14726         editor.on('editorevent', this.updateToolbar, this);
14727     },
14728     onBtnClick : function(id)
14729     {
14730        this.editorcore.relayCmd(id);
14731        this.editorcore.focus();
14732     },
14733     
14734     /**
14735      * Protected method that will not generally be called directly. It triggers
14736      * a toolbar update by reading the markup state of the current selection in the editor.
14737      */
14738     updateToolbar: function(){
14739
14740         if(!this.editorcore.activated){
14741             this.editor.onFirstFocus(); // is this neeed?
14742             return;
14743         }
14744
14745         var btns = this.buttons; 
14746         var doc = this.editorcore.doc;
14747         btns.get('bold').setActive(doc.queryCommandState('bold'));
14748         btns.get('italic').setActive(doc.queryCommandState('italic'));
14749         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14750         
14751         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14752         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14753         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14754         
14755         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14756         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14757          /*
14758         
14759         var ans = this.editorcore.getAllAncestors();
14760         if (this.formatCombo) {
14761             
14762             
14763             var store = this.formatCombo.store;
14764             this.formatCombo.setValue("");
14765             for (var i =0; i < ans.length;i++) {
14766                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14767                     // select it..
14768                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14769                     break;
14770                 }
14771             }
14772         }
14773         
14774         
14775         
14776         // hides menus... - so this cant be on a menu...
14777         Roo.bootstrap.MenuMgr.hideAll();
14778         */
14779         Roo.bootstrap.MenuMgr.hideAll();
14780         //this.editorsyncValue();
14781     },
14782     onFirstFocus: function() {
14783         this.buttons.each(function(item){
14784            item.enable();
14785         });
14786     },
14787     toggleSourceEdit : function(sourceEditMode){
14788         
14789           
14790         if(sourceEditMode){
14791             Roo.log("disabling buttons");
14792            this.buttons.each( function(item){
14793                 if(item.cmd != 'pencil'){
14794                     item.disable();
14795                 }
14796             });
14797           
14798         }else{
14799             Roo.log("enabling buttons");
14800             if(this.editorcore.initialized){
14801                 this.buttons.each( function(item){
14802                     item.enable();
14803                 });
14804             }
14805             
14806         }
14807         Roo.log("calling toggole on editor");
14808         // tell the editor that it's been pressed..
14809         this.editor.toggleSourceEdit(sourceEditMode);
14810        
14811     }
14812 });
14813
14814
14815
14816
14817
14818 /**
14819  * @class Roo.bootstrap.Table.AbstractSelectionModel
14820  * @extends Roo.util.Observable
14821  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14822  * implemented by descendant classes.  This class should not be directly instantiated.
14823  * @constructor
14824  */
14825 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14826     this.locked = false;
14827     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14828 };
14829
14830
14831 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14832     /** @ignore Called by the grid automatically. Do not call directly. */
14833     init : function(grid){
14834         this.grid = grid;
14835         this.initEvents();
14836     },
14837
14838     /**
14839      * Locks the selections.
14840      */
14841     lock : function(){
14842         this.locked = true;
14843     },
14844
14845     /**
14846      * Unlocks the selections.
14847      */
14848     unlock : function(){
14849         this.locked = false;
14850     },
14851
14852     /**
14853      * Returns true if the selections are locked.
14854      * @return {Boolean}
14855      */
14856     isLocked : function(){
14857         return this.locked;
14858     }
14859 });
14860 /**
14861  * @class Roo.bootstrap.Table.ColumnModel
14862  * @extends Roo.util.Observable
14863  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14864  * the columns in the table.
14865  
14866  * @constructor
14867  * @param {Object} config An Array of column config objects. See this class's
14868  * config objects for details.
14869 */
14870 Roo.bootstrap.Table.ColumnModel = function(config){
14871         /**
14872      * The config passed into the constructor
14873      */
14874     this.config = config;
14875     this.lookup = {};
14876
14877     // if no id, create one
14878     // if the column does not have a dataIndex mapping,
14879     // map it to the order it is in the config
14880     for(var i = 0, len = config.length; i < len; i++){
14881         var c = config[i];
14882         if(typeof c.dataIndex == "undefined"){
14883             c.dataIndex = i;
14884         }
14885         if(typeof c.renderer == "string"){
14886             c.renderer = Roo.util.Format[c.renderer];
14887         }
14888         if(typeof c.id == "undefined"){
14889             c.id = Roo.id();
14890         }
14891 //        if(c.editor && c.editor.xtype){
14892 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14893 //        }
14894 //        if(c.editor && c.editor.isFormField){
14895 //            c.editor = new Roo.grid.GridEditor(c.editor);
14896 //        }
14897
14898         this.lookup[c.id] = c;
14899     }
14900
14901     /**
14902      * The width of columns which have no width specified (defaults to 100)
14903      * @type Number
14904      */
14905     this.defaultWidth = 100;
14906
14907     /**
14908      * Default sortable of columns which have no sortable specified (defaults to false)
14909      * @type Boolean
14910      */
14911     this.defaultSortable = false;
14912
14913     this.addEvents({
14914         /**
14915              * @event widthchange
14916              * Fires when the width of a column changes.
14917              * @param {ColumnModel} this
14918              * @param {Number} columnIndex The column index
14919              * @param {Number} newWidth The new width
14920              */
14921             "widthchange": true,
14922         /**
14923              * @event headerchange
14924              * Fires when the text of a header changes.
14925              * @param {ColumnModel} this
14926              * @param {Number} columnIndex The column index
14927              * @param {Number} newText The new header text
14928              */
14929             "headerchange": true,
14930         /**
14931              * @event hiddenchange
14932              * Fires when a column is hidden or "unhidden".
14933              * @param {ColumnModel} this
14934              * @param {Number} columnIndex The column index
14935              * @param {Boolean} hidden true if hidden, false otherwise
14936              */
14937             "hiddenchange": true,
14938             /**
14939          * @event columnmoved
14940          * Fires when a column is moved.
14941          * @param {ColumnModel} this
14942          * @param {Number} oldIndex
14943          * @param {Number} newIndex
14944          */
14945         "columnmoved" : true,
14946         /**
14947          * @event columlockchange
14948          * Fires when a column's locked state is changed
14949          * @param {ColumnModel} this
14950          * @param {Number} colIndex
14951          * @param {Boolean} locked true if locked
14952          */
14953         "columnlockchange" : true
14954     });
14955     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14956 };
14957 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14958     /**
14959      * @cfg {String} header The header text to display in the Grid view.
14960      */
14961     /**
14962      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14963      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14964      * specified, the column's index is used as an index into the Record's data Array.
14965      */
14966     /**
14967      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14968      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14969      */
14970     /**
14971      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14972      * Defaults to the value of the {@link #defaultSortable} property.
14973      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14974      */
14975     /**
14976      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14977      */
14978     /**
14979      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14980      */
14981     /**
14982      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14983      */
14984     /**
14985      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14986      */
14987     /**
14988      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14989      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14990      * default renderer uses the raw data value.
14991      */
14992     /**
14993      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14994      */
14995
14996     /**
14997      * Returns the id of the column at the specified index.
14998      * @param {Number} index The column index
14999      * @return {String} the id
15000      */
15001     getColumnId : function(index){
15002         return this.config[index].id;
15003     },
15004
15005     /**
15006      * Returns the column for a specified id.
15007      * @param {String} id The column id
15008      * @return {Object} the column
15009      */
15010     getColumnById : function(id){
15011         return this.lookup[id];
15012     },
15013
15014     
15015     /**
15016      * Returns the column for a specified dataIndex.
15017      * @param {String} dataIndex The column dataIndex
15018      * @return {Object|Boolean} the column or false if not found
15019      */
15020     getColumnByDataIndex: function(dataIndex){
15021         var index = this.findColumnIndex(dataIndex);
15022         return index > -1 ? this.config[index] : false;
15023     },
15024     
15025     /**
15026      * Returns the index for a specified column id.
15027      * @param {String} id The column id
15028      * @return {Number} the index, or -1 if not found
15029      */
15030     getIndexById : function(id){
15031         for(var i = 0, len = this.config.length; i < len; i++){
15032             if(this.config[i].id == id){
15033                 return i;
15034             }
15035         }
15036         return -1;
15037     },
15038     
15039     /**
15040      * Returns the index for a specified column dataIndex.
15041      * @param {String} dataIndex The column dataIndex
15042      * @return {Number} the index, or -1 if not found
15043      */
15044     
15045     findColumnIndex : function(dataIndex){
15046         for(var i = 0, len = this.config.length; i < len; i++){
15047             if(this.config[i].dataIndex == dataIndex){
15048                 return i;
15049             }
15050         }
15051         return -1;
15052     },
15053     
15054     
15055     moveColumn : function(oldIndex, newIndex){
15056         var c = this.config[oldIndex];
15057         this.config.splice(oldIndex, 1);
15058         this.config.splice(newIndex, 0, c);
15059         this.dataMap = null;
15060         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15061     },
15062
15063     isLocked : function(colIndex){
15064         return this.config[colIndex].locked === true;
15065     },
15066
15067     setLocked : function(colIndex, value, suppressEvent){
15068         if(this.isLocked(colIndex) == value){
15069             return;
15070         }
15071         this.config[colIndex].locked = value;
15072         if(!suppressEvent){
15073             this.fireEvent("columnlockchange", this, colIndex, value);
15074         }
15075     },
15076
15077     getTotalLockedWidth : function(){
15078         var totalWidth = 0;
15079         for(var i = 0; i < this.config.length; i++){
15080             if(this.isLocked(i) && !this.isHidden(i)){
15081                 this.totalWidth += this.getColumnWidth(i);
15082             }
15083         }
15084         return totalWidth;
15085     },
15086
15087     getLockedCount : function(){
15088         for(var i = 0, len = this.config.length; i < len; i++){
15089             if(!this.isLocked(i)){
15090                 return i;
15091             }
15092         }
15093     },
15094
15095     /**
15096      * Returns the number of columns.
15097      * @return {Number}
15098      */
15099     getColumnCount : function(visibleOnly){
15100         if(visibleOnly === true){
15101             var c = 0;
15102             for(var i = 0, len = this.config.length; i < len; i++){
15103                 if(!this.isHidden(i)){
15104                     c++;
15105                 }
15106             }
15107             return c;
15108         }
15109         return this.config.length;
15110     },
15111
15112     /**
15113      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15114      * @param {Function} fn
15115      * @param {Object} scope (optional)
15116      * @return {Array} result
15117      */
15118     getColumnsBy : function(fn, scope){
15119         var r = [];
15120         for(var i = 0, len = this.config.length; i < len; i++){
15121             var c = this.config[i];
15122             if(fn.call(scope||this, c, i) === true){
15123                 r[r.length] = c;
15124             }
15125         }
15126         return r;
15127     },
15128
15129     /**
15130      * Returns true if the specified column is sortable.
15131      * @param {Number} col The column index
15132      * @return {Boolean}
15133      */
15134     isSortable : function(col){
15135         if(typeof this.config[col].sortable == "undefined"){
15136             return this.defaultSortable;
15137         }
15138         return this.config[col].sortable;
15139     },
15140
15141     /**
15142      * Returns the rendering (formatting) function defined for the column.
15143      * @param {Number} col The column index.
15144      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15145      */
15146     getRenderer : function(col){
15147         if(!this.config[col].renderer){
15148             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15149         }
15150         return this.config[col].renderer;
15151     },
15152
15153     /**
15154      * Sets the rendering (formatting) function for a column.
15155      * @param {Number} col The column index
15156      * @param {Function} fn The function to use to process the cell's raw data
15157      * to return HTML markup for the grid view. The render function is called with
15158      * the following parameters:<ul>
15159      * <li>Data value.</li>
15160      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15161      * <li>css A CSS style string to apply to the table cell.</li>
15162      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15163      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15164      * <li>Row index</li>
15165      * <li>Column index</li>
15166      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15167      */
15168     setRenderer : function(col, fn){
15169         this.config[col].renderer = fn;
15170     },
15171
15172     /**
15173      * Returns the width for the specified column.
15174      * @param {Number} col The column index
15175      * @return {Number}
15176      */
15177     getColumnWidth : function(col){
15178         return this.config[col].width * 1 || this.defaultWidth;
15179     },
15180
15181     /**
15182      * Sets the width for a column.
15183      * @param {Number} col The column index
15184      * @param {Number} width The new width
15185      */
15186     setColumnWidth : function(col, width, suppressEvent){
15187         this.config[col].width = width;
15188         this.totalWidth = null;
15189         if(!suppressEvent){
15190              this.fireEvent("widthchange", this, col, width);
15191         }
15192     },
15193
15194     /**
15195      * Returns the total width of all columns.
15196      * @param {Boolean} includeHidden True to include hidden column widths
15197      * @return {Number}
15198      */
15199     getTotalWidth : function(includeHidden){
15200         if(!this.totalWidth){
15201             this.totalWidth = 0;
15202             for(var i = 0, len = this.config.length; i < len; i++){
15203                 if(includeHidden || !this.isHidden(i)){
15204                     this.totalWidth += this.getColumnWidth(i);
15205                 }
15206             }
15207         }
15208         return this.totalWidth;
15209     },
15210
15211     /**
15212      * Returns the header for the specified column.
15213      * @param {Number} col The column index
15214      * @return {String}
15215      */
15216     getColumnHeader : function(col){
15217         return this.config[col].header;
15218     },
15219
15220     /**
15221      * Sets the header for a column.
15222      * @param {Number} col The column index
15223      * @param {String} header The new header
15224      */
15225     setColumnHeader : function(col, header){
15226         this.config[col].header = header;
15227         this.fireEvent("headerchange", this, col, header);
15228     },
15229
15230     /**
15231      * Returns the tooltip for the specified column.
15232      * @param {Number} col The column index
15233      * @return {String}
15234      */
15235     getColumnTooltip : function(col){
15236             return this.config[col].tooltip;
15237     },
15238     /**
15239      * Sets the tooltip for a column.
15240      * @param {Number} col The column index
15241      * @param {String} tooltip The new tooltip
15242      */
15243     setColumnTooltip : function(col, tooltip){
15244             this.config[col].tooltip = tooltip;
15245     },
15246
15247     /**
15248      * Returns the dataIndex for the specified column.
15249      * @param {Number} col The column index
15250      * @return {Number}
15251      */
15252     getDataIndex : function(col){
15253         return this.config[col].dataIndex;
15254     },
15255
15256     /**
15257      * Sets the dataIndex for a column.
15258      * @param {Number} col The column index
15259      * @param {Number} dataIndex The new dataIndex
15260      */
15261     setDataIndex : function(col, dataIndex){
15262         this.config[col].dataIndex = dataIndex;
15263     },
15264
15265     
15266     
15267     /**
15268      * Returns true if the cell is editable.
15269      * @param {Number} colIndex The column index
15270      * @param {Number} rowIndex The row index
15271      * @return {Boolean}
15272      */
15273     isCellEditable : function(colIndex, rowIndex){
15274         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15275     },
15276
15277     /**
15278      * Returns the editor defined for the cell/column.
15279      * return false or null to disable editing.
15280      * @param {Number} colIndex The column index
15281      * @param {Number} rowIndex The row index
15282      * @return {Object}
15283      */
15284     getCellEditor : function(colIndex, rowIndex){
15285         return this.config[colIndex].editor;
15286     },
15287
15288     /**
15289      * Sets if a column is editable.
15290      * @param {Number} col The column index
15291      * @param {Boolean} editable True if the column is editable
15292      */
15293     setEditable : function(col, editable){
15294         this.config[col].editable = editable;
15295     },
15296
15297
15298     /**
15299      * Returns true if the column is hidden.
15300      * @param {Number} colIndex The column index
15301      * @return {Boolean}
15302      */
15303     isHidden : function(colIndex){
15304         return this.config[colIndex].hidden;
15305     },
15306
15307
15308     /**
15309      * Returns true if the column width cannot be changed
15310      */
15311     isFixed : function(colIndex){
15312         return this.config[colIndex].fixed;
15313     },
15314
15315     /**
15316      * Returns true if the column can be resized
15317      * @return {Boolean}
15318      */
15319     isResizable : function(colIndex){
15320         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15321     },
15322     /**
15323      * Sets if a column is hidden.
15324      * @param {Number} colIndex The column index
15325      * @param {Boolean} hidden True if the column is hidden
15326      */
15327     setHidden : function(colIndex, hidden){
15328         this.config[colIndex].hidden = hidden;
15329         this.totalWidth = null;
15330         this.fireEvent("hiddenchange", this, colIndex, hidden);
15331     },
15332
15333     /**
15334      * Sets the editor for a column.
15335      * @param {Number} col The column index
15336      * @param {Object} editor The editor object
15337      */
15338     setEditor : function(col, editor){
15339         this.config[col].editor = editor;
15340     }
15341 });
15342
15343 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15344         if(typeof value == "string" && value.length < 1){
15345             return "&#160;";
15346         }
15347         return value;
15348 };
15349
15350 // Alias for backwards compatibility
15351 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15352
15353 /**
15354  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15355  * @class Roo.bootstrap.Table.RowSelectionModel
15356  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15357  * It supports multiple selections and keyboard selection/navigation. 
15358  * @constructor
15359  * @param {Object} config
15360  */
15361
15362 Roo.bootstrap.Table.RowSelectionModel = function(config){
15363     Roo.apply(this, config);
15364     this.selections = new Roo.util.MixedCollection(false, function(o){
15365         return o.id;
15366     });
15367
15368     this.last = false;
15369     this.lastActive = false;
15370
15371     this.addEvents({
15372         /**
15373              * @event selectionchange
15374              * Fires when the selection changes
15375              * @param {SelectionModel} this
15376              */
15377             "selectionchange" : true,
15378         /**
15379              * @event afterselectionchange
15380              * Fires after the selection changes (eg. by key press or clicking)
15381              * @param {SelectionModel} this
15382              */
15383             "afterselectionchange" : true,
15384         /**
15385              * @event beforerowselect
15386              * Fires when a row is selected being selected, return false to cancel.
15387              * @param {SelectionModel} this
15388              * @param {Number} rowIndex The selected index
15389              * @param {Boolean} keepExisting False if other selections will be cleared
15390              */
15391             "beforerowselect" : true,
15392         /**
15393              * @event rowselect
15394              * Fires when a row is selected.
15395              * @param {SelectionModel} this
15396              * @param {Number} rowIndex The selected index
15397              * @param {Roo.data.Record} r The record
15398              */
15399             "rowselect" : true,
15400         /**
15401              * @event rowdeselect
15402              * Fires when a row is deselected.
15403              * @param {SelectionModel} this
15404              * @param {Number} rowIndex The selected index
15405              */
15406         "rowdeselect" : true
15407     });
15408     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15409     this.locked = false;
15410 };
15411
15412 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15413     /**
15414      * @cfg {Boolean} singleSelect
15415      * True to allow selection of only one row at a time (defaults to false)
15416      */
15417     singleSelect : false,
15418
15419     // private
15420     initEvents : function(){
15421
15422         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15423             this.grid.on("mousedown", this.handleMouseDown, this);
15424         }else{ // allow click to work like normal
15425             this.grid.on("rowclick", this.handleDragableRowClick, this);
15426         }
15427
15428         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15429             "up" : function(e){
15430                 if(!e.shiftKey){
15431                     this.selectPrevious(e.shiftKey);
15432                 }else if(this.last !== false && this.lastActive !== false){
15433                     var last = this.last;
15434                     this.selectRange(this.last,  this.lastActive-1);
15435                     this.grid.getView().focusRow(this.lastActive);
15436                     if(last !== false){
15437                         this.last = last;
15438                     }
15439                 }else{
15440                     this.selectFirstRow();
15441                 }
15442                 this.fireEvent("afterselectionchange", this);
15443             },
15444             "down" : function(e){
15445                 if(!e.shiftKey){
15446                     this.selectNext(e.shiftKey);
15447                 }else if(this.last !== false && this.lastActive !== false){
15448                     var last = this.last;
15449                     this.selectRange(this.last,  this.lastActive+1);
15450                     this.grid.getView().focusRow(this.lastActive);
15451                     if(last !== false){
15452                         this.last = last;
15453                     }
15454                 }else{
15455                     this.selectFirstRow();
15456                 }
15457                 this.fireEvent("afterselectionchange", this);
15458             },
15459             scope: this
15460         });
15461
15462         var view = this.grid.view;
15463         view.on("refresh", this.onRefresh, this);
15464         view.on("rowupdated", this.onRowUpdated, this);
15465         view.on("rowremoved", this.onRemove, this);
15466     },
15467
15468     // private
15469     onRefresh : function(){
15470         var ds = this.grid.dataSource, i, v = this.grid.view;
15471         var s = this.selections;
15472         s.each(function(r){
15473             if((i = ds.indexOfId(r.id)) != -1){
15474                 v.onRowSelect(i);
15475             }else{
15476                 s.remove(r);
15477             }
15478         });
15479     },
15480
15481     // private
15482     onRemove : function(v, index, r){
15483         this.selections.remove(r);
15484     },
15485
15486     // private
15487     onRowUpdated : function(v, index, r){
15488         if(this.isSelected(r)){
15489             v.onRowSelect(index);
15490         }
15491     },
15492
15493     /**
15494      * Select records.
15495      * @param {Array} records The records to select
15496      * @param {Boolean} keepExisting (optional) True to keep existing selections
15497      */
15498     selectRecords : function(records, keepExisting){
15499         if(!keepExisting){
15500             this.clearSelections();
15501         }
15502         var ds = this.grid.dataSource;
15503         for(var i = 0, len = records.length; i < len; i++){
15504             this.selectRow(ds.indexOf(records[i]), true);
15505         }
15506     },
15507
15508     /**
15509      * Gets the number of selected rows.
15510      * @return {Number}
15511      */
15512     getCount : function(){
15513         return this.selections.length;
15514     },
15515
15516     /**
15517      * Selects the first row in the grid.
15518      */
15519     selectFirstRow : function(){
15520         this.selectRow(0);
15521     },
15522
15523     /**
15524      * Select the last row.
15525      * @param {Boolean} keepExisting (optional) True to keep existing selections
15526      */
15527     selectLastRow : function(keepExisting){
15528         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15529     },
15530
15531     /**
15532      * Selects the row immediately following the last selected row.
15533      * @param {Boolean} keepExisting (optional) True to keep existing selections
15534      */
15535     selectNext : function(keepExisting){
15536         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15537             this.selectRow(this.last+1, keepExisting);
15538             this.grid.getView().focusRow(this.last);
15539         }
15540     },
15541
15542     /**
15543      * Selects the row that precedes the last selected row.
15544      * @param {Boolean} keepExisting (optional) True to keep existing selections
15545      */
15546     selectPrevious : function(keepExisting){
15547         if(this.last){
15548             this.selectRow(this.last-1, keepExisting);
15549             this.grid.getView().focusRow(this.last);
15550         }
15551     },
15552
15553     /**
15554      * Returns the selected records
15555      * @return {Array} Array of selected records
15556      */
15557     getSelections : function(){
15558         return [].concat(this.selections.items);
15559     },
15560
15561     /**
15562      * Returns the first selected record.
15563      * @return {Record}
15564      */
15565     getSelected : function(){
15566         return this.selections.itemAt(0);
15567     },
15568
15569
15570     /**
15571      * Clears all selections.
15572      */
15573     clearSelections : function(fast){
15574         if(this.locked) return;
15575         if(fast !== true){
15576             var ds = this.grid.dataSource;
15577             var s = this.selections;
15578             s.each(function(r){
15579                 this.deselectRow(ds.indexOfId(r.id));
15580             }, this);
15581             s.clear();
15582         }else{
15583             this.selections.clear();
15584         }
15585         this.last = false;
15586     },
15587
15588
15589     /**
15590      * Selects all rows.
15591      */
15592     selectAll : function(){
15593         if(this.locked) return;
15594         this.selections.clear();
15595         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15596             this.selectRow(i, true);
15597         }
15598     },
15599
15600     /**
15601      * Returns True if there is a selection.
15602      * @return {Boolean}
15603      */
15604     hasSelection : function(){
15605         return this.selections.length > 0;
15606     },
15607
15608     /**
15609      * Returns True if the specified row is selected.
15610      * @param {Number/Record} record The record or index of the record to check
15611      * @return {Boolean}
15612      */
15613     isSelected : function(index){
15614         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15615         return (r && this.selections.key(r.id) ? true : false);
15616     },
15617
15618     /**
15619      * Returns True if the specified record id is selected.
15620      * @param {String} id The id of record to check
15621      * @return {Boolean}
15622      */
15623     isIdSelected : function(id){
15624         return (this.selections.key(id) ? true : false);
15625     },
15626
15627     // private
15628     handleMouseDown : function(e, t){
15629         var view = this.grid.getView(), rowIndex;
15630         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15631             return;
15632         };
15633         if(e.shiftKey && this.last !== false){
15634             var last = this.last;
15635             this.selectRange(last, rowIndex, e.ctrlKey);
15636             this.last = last; // reset the last
15637             view.focusRow(rowIndex);
15638         }else{
15639             var isSelected = this.isSelected(rowIndex);
15640             if(e.button !== 0 && isSelected){
15641                 view.focusRow(rowIndex);
15642             }else if(e.ctrlKey && isSelected){
15643                 this.deselectRow(rowIndex);
15644             }else if(!isSelected){
15645                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15646                 view.focusRow(rowIndex);
15647             }
15648         }
15649         this.fireEvent("afterselectionchange", this);
15650     },
15651     // private
15652     handleDragableRowClick :  function(grid, rowIndex, e) 
15653     {
15654         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15655             this.selectRow(rowIndex, false);
15656             grid.view.focusRow(rowIndex);
15657              this.fireEvent("afterselectionchange", this);
15658         }
15659     },
15660     
15661     /**
15662      * Selects multiple rows.
15663      * @param {Array} rows Array of the indexes of the row to select
15664      * @param {Boolean} keepExisting (optional) True to keep existing selections
15665      */
15666     selectRows : function(rows, keepExisting){
15667         if(!keepExisting){
15668             this.clearSelections();
15669         }
15670         for(var i = 0, len = rows.length; i < len; i++){
15671             this.selectRow(rows[i], true);
15672         }
15673     },
15674
15675     /**
15676      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15677      * @param {Number} startRow The index of the first row in the range
15678      * @param {Number} endRow The index of the last row in the range
15679      * @param {Boolean} keepExisting (optional) True to retain existing selections
15680      */
15681     selectRange : function(startRow, endRow, keepExisting){
15682         if(this.locked) return;
15683         if(!keepExisting){
15684             this.clearSelections();
15685         }
15686         if(startRow <= endRow){
15687             for(var i = startRow; i <= endRow; i++){
15688                 this.selectRow(i, true);
15689             }
15690         }else{
15691             for(var i = startRow; i >= endRow; i--){
15692                 this.selectRow(i, true);
15693             }
15694         }
15695     },
15696
15697     /**
15698      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15699      * @param {Number} startRow The index of the first row in the range
15700      * @param {Number} endRow The index of the last row in the range
15701      */
15702     deselectRange : function(startRow, endRow, preventViewNotify){
15703         if(this.locked) return;
15704         for(var i = startRow; i <= endRow; i++){
15705             this.deselectRow(i, preventViewNotify);
15706         }
15707     },
15708
15709     /**
15710      * Selects a row.
15711      * @param {Number} row The index of the row to select
15712      * @param {Boolean} keepExisting (optional) True to keep existing selections
15713      */
15714     selectRow : function(index, keepExisting, preventViewNotify){
15715         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15716         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15717             if(!keepExisting || this.singleSelect){
15718                 this.clearSelections();
15719             }
15720             var r = this.grid.dataSource.getAt(index);
15721             this.selections.add(r);
15722             this.last = this.lastActive = index;
15723             if(!preventViewNotify){
15724                 this.grid.getView().onRowSelect(index);
15725             }
15726             this.fireEvent("rowselect", this, index, r);
15727             this.fireEvent("selectionchange", this);
15728         }
15729     },
15730
15731     /**
15732      * Deselects a row.
15733      * @param {Number} row The index of the row to deselect
15734      */
15735     deselectRow : function(index, preventViewNotify){
15736         if(this.locked) return;
15737         if(this.last == index){
15738             this.last = false;
15739         }
15740         if(this.lastActive == index){
15741             this.lastActive = false;
15742         }
15743         var r = this.grid.dataSource.getAt(index);
15744         this.selections.remove(r);
15745         if(!preventViewNotify){
15746             this.grid.getView().onRowDeselect(index);
15747         }
15748         this.fireEvent("rowdeselect", this, index);
15749         this.fireEvent("selectionchange", this);
15750     },
15751
15752     // private
15753     restoreLast : function(){
15754         if(this._last){
15755             this.last = this._last;
15756         }
15757     },
15758
15759     // private
15760     acceptsNav : function(row, col, cm){
15761         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15762     },
15763
15764     // private
15765     onEditorKey : function(field, e){
15766         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15767         if(k == e.TAB){
15768             e.stopEvent();
15769             ed.completeEdit();
15770             if(e.shiftKey){
15771                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15772             }else{
15773                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15774             }
15775         }else if(k == e.ENTER && !e.ctrlKey){
15776             e.stopEvent();
15777             ed.completeEdit();
15778             if(e.shiftKey){
15779                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15780             }else{
15781                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15782             }
15783         }else if(k == e.ESC){
15784             ed.cancelEdit();
15785         }
15786         if(newCell){
15787             g.startEditing(newCell[0], newCell[1]);
15788         }
15789     }
15790 });/*
15791  * - LGPL
15792  *
15793  * element
15794  * 
15795  */
15796
15797 /**
15798  * @class Roo.bootstrap.MessageBar
15799  * @extends Roo.bootstrap.Component
15800  * Bootstrap MessageBar class
15801  * @cfg {String} html contents of the MessageBar
15802  * @cfg {String} weight (info | success | warning | danger) default info
15803  * @cfg {String} beforeClass insert the bar before the given class
15804  * @cfg {Boolean} closable (true | false) default false
15805  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15806  * 
15807  * @constructor
15808  * Create a new Element
15809  * @param {Object} config The config object
15810  */
15811
15812 Roo.bootstrap.MessageBar = function(config){
15813     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15814 };
15815
15816 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15817     
15818     html: '',
15819     weight: 'info',
15820     closable: false,
15821     fixed: false,
15822     beforeClass: 'bootstrap-sticky-wrap',
15823     
15824     getAutoCreate : function(){
15825         
15826         var cfg = {
15827             tag: 'div',
15828             cls: 'alert alert-dismissable alert-' + this.weight,
15829             cn: [
15830                 {
15831                     tag: 'span',
15832                     cls: 'message',
15833                     html: this.html || ''
15834                 }
15835             ]
15836         }
15837         
15838         if(this.fixed){
15839             cfg.cls += ' alert-messages-fixed';
15840         }
15841         
15842         if(this.closable){
15843             cfg.cn.push({
15844                 tag: 'button',
15845                 cls: 'close',
15846                 html: 'x'
15847             });
15848         }
15849         
15850         return cfg;
15851     },
15852     
15853     onRender : function(ct, position)
15854     {
15855         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15856         
15857         if(!this.el){
15858             var cfg = Roo.apply({},  this.getAutoCreate());
15859             cfg.id = Roo.id();
15860             
15861             if (this.cls) {
15862                 cfg.cls += ' ' + this.cls;
15863             }
15864             if (this.style) {
15865                 cfg.style = this.style;
15866             }
15867             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15868             
15869             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15870         }
15871         
15872         this.el.select('>button.close').on('click', this.hide, this);
15873         
15874     },
15875     
15876     show : function()
15877     {
15878         if (!this.rendered) {
15879             this.render();
15880         }
15881         
15882         this.el.show();
15883         
15884         this.fireEvent('show', this);
15885         
15886     },
15887     
15888     hide : function()
15889     {
15890         if (!this.rendered) {
15891             this.render();
15892         }
15893         
15894         this.el.hide();
15895         
15896         this.fireEvent('hide', this);
15897     },
15898     
15899     update : function()
15900     {
15901 //        var e = this.el.dom.firstChild;
15902 //        
15903 //        if(this.closable){
15904 //            e = e.nextSibling;
15905 //        }
15906 //        
15907 //        e.data = this.html || '';
15908
15909         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15910     }
15911    
15912 });
15913
15914  
15915
15916