Roo/form/ComboBoxArray.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             if(typeof(config.width) != 'undefined'){
3018                 c.style = 'width:' + config.width + 'px';
3019             }
3020             
3021             header.cn.push(c)
3022         }
3023         
3024         return header;
3025     },
3026     
3027     renderBody : function()
3028     {
3029         var body = {
3030             tag: 'tbody',
3031             cn : []
3032         };
3033         
3034         return body;
3035     },
3036     
3037     renderFooter : function()
3038     {
3039         var footer = {
3040             tag: 'tfoot',
3041             cn : []
3042         };
3043         
3044         return footer;
3045     },
3046     
3047     onLoad : function()
3048     {
3049         Roo.log('ds onload');
3050         
3051         var _this = this;
3052         var cm = this.cm;
3053         
3054         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3055             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3056             
3057             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3058                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3059             }
3060             
3061             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3062                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3063             }
3064         });
3065         
3066         var tbody = this.el.select('tbody', true).first();
3067         
3068         var renders = [];
3069         
3070         if(this.store.getCount() > 0){
3071             this.store.data.each(function(d){
3072                 var row = {
3073                     tag : 'tr',
3074                     cn : []
3075                 };
3076                 
3077                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3078                     var renderer = cm.getRenderer(i);
3079                     var config = cm.config[i];
3080                     var value = '';
3081                     var id = Roo.id();
3082                     
3083                     if(typeof(renderer) !== 'undefined'){
3084                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3085                     }
3086                     
3087                     if(typeof(value) === 'object'){
3088                         renders.push({
3089                             id : id,
3090                             cfg : value 
3091                         })
3092                     }
3093                     
3094                     var td = {
3095                         tag: 'td',
3096                         id: id,
3097                         html: (typeof(value) === 'object') ? '' : value
3098                     };
3099                     
3100                     if(typeof(config.width) != 'undefined'){
3101                         td.style = 'width:' +  config.width + 'px';
3102                     }
3103                     
3104                     row.cn.push(td);
3105                    
3106                 }
3107                 
3108                 tbody.createChild(row);
3109                 
3110             });
3111         }
3112         
3113         
3114         if(renders.length){
3115             var _this = this;
3116             Roo.each(renders, function(r){
3117                 _this.renderColumn(r);
3118             })
3119         }
3120 //        
3121 //        if(this.loadMask){
3122 //            this.maskEl.hide();
3123 //        }
3124     },
3125     
3126     onBeforeLoad : function()
3127     {
3128         Roo.log('ds onBeforeLoad');
3129         
3130         this.clear();
3131         
3132 //        if(this.loadMask){
3133 //            this.maskEl.show();
3134 //        }
3135     },
3136     
3137     clear : function()
3138     {
3139         this.el.select('tbody', true).first().dom.innerHTML = '';
3140     },
3141     
3142     getSelectionModel : function(){
3143         if(!this.selModel){
3144             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3145         }
3146         return this.selModel;
3147     },
3148     
3149     renderColumn : function(r)
3150     {
3151         var _this = this;
3152         r.cfg.render(Roo.get(r.id));
3153         
3154         if(r.cfg.cn){
3155             Roo.each(r.cfg.cn, function(c){
3156                 var child = {
3157                     id: r.id,
3158                     cfg: c
3159                 }
3160                 _this.renderColumn(child);
3161             })
3162         }
3163     }
3164    
3165 });
3166
3167  
3168
3169  /*
3170  * - LGPL
3171  *
3172  * table cell
3173  * 
3174  */
3175
3176 /**
3177  * @class Roo.bootstrap.TableCell
3178  * @extends Roo.bootstrap.Component
3179  * Bootstrap TableCell class
3180  * @cfg {String} html cell contain text
3181  * @cfg {String} cls cell class
3182  * @cfg {String} tag cell tag (td|th) default td
3183  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3184  * @cfg {String} align Aligns the content in a cell
3185  * @cfg {String} axis Categorizes cells
3186  * @cfg {String} bgcolor Specifies the background color of a cell
3187  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3188  * @cfg {Number} colspan Specifies the number of columns a cell should span
3189  * @cfg {String} headers Specifies one or more header cells a cell is related to
3190  * @cfg {Number} height Sets the height of a cell
3191  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3192  * @cfg {Number} rowspan Sets the number of rows a cell should span
3193  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3194  * @cfg {String} valign Vertical aligns the content in a cell
3195  * @cfg {Number} width Specifies the width of a cell
3196  * 
3197  * @constructor
3198  * Create a new TableCell
3199  * @param {Object} config The config object
3200  */
3201
3202 Roo.bootstrap.TableCell = function(config){
3203     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3204 };
3205
3206 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3207     
3208     html: false,
3209     cls: false,
3210     tag: false,
3211     abbr: false,
3212     align: false,
3213     axis: false,
3214     bgcolor: false,
3215     charoff: false,
3216     colspan: false,
3217     headers: false,
3218     height: false,
3219     nowrap: false,
3220     rowspan: false,
3221     scope: false,
3222     valign: false,
3223     width: false,
3224     
3225     
3226     getAutoCreate : function(){
3227         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3228         
3229         cfg = {
3230             tag: 'td'
3231         }
3232         
3233         if(this.tag){
3234             cfg.tag = this.tag;
3235         }
3236         
3237         if (this.html) {
3238             cfg.html=this.html
3239         }
3240         if (this.cls) {
3241             cfg.cls=this.cls
3242         }
3243         if (this.abbr) {
3244             cfg.abbr=this.abbr
3245         }
3246         if (this.align) {
3247             cfg.align=this.align
3248         }
3249         if (this.axis) {
3250             cfg.axis=this.axis
3251         }
3252         if (this.bgcolor) {
3253             cfg.bgcolor=this.bgcolor
3254         }
3255         if (this.charoff) {
3256             cfg.charoff=this.charoff
3257         }
3258         if (this.colspan) {
3259             cfg.colspan=this.colspan
3260         }
3261         if (this.headers) {
3262             cfg.headers=this.headers
3263         }
3264         if (this.height) {
3265             cfg.height=this.height
3266         }
3267         if (this.nowrap) {
3268             cfg.nowrap=this.nowrap
3269         }
3270         if (this.rowspan) {
3271             cfg.rowspan=this.rowspan
3272         }
3273         if (this.scope) {
3274             cfg.scope=this.scope
3275         }
3276         if (this.valign) {
3277             cfg.valign=this.valign
3278         }
3279         if (this.width) {
3280             cfg.width=this.width
3281         }
3282         
3283         
3284         return cfg;
3285     }
3286    
3287 });
3288
3289  
3290
3291  /*
3292  * - LGPL
3293  *
3294  * table row
3295  * 
3296  */
3297
3298 /**
3299  * @class Roo.bootstrap.TableRow
3300  * @extends Roo.bootstrap.Component
3301  * Bootstrap TableRow class
3302  * @cfg {String} cls row class
3303  * @cfg {String} align Aligns the content in a table row
3304  * @cfg {String} bgcolor Specifies a background color for a table row
3305  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3306  * @cfg {String} valign Vertical aligns the content in a table row
3307  * 
3308  * @constructor
3309  * Create a new TableRow
3310  * @param {Object} config The config object
3311  */
3312
3313 Roo.bootstrap.TableRow = function(config){
3314     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3315 };
3316
3317 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3318     
3319     cls: false,
3320     align: false,
3321     bgcolor: false,
3322     charoff: false,
3323     valign: false,
3324     
3325     getAutoCreate : function(){
3326         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3327         
3328         cfg = {
3329             tag: 'tr'
3330         }
3331             
3332         if(this.cls){
3333             cfg.cls = this.cls;
3334         }
3335         if(this.align){
3336             cfg.align = this.align;
3337         }
3338         if(this.bgcolor){
3339             cfg.bgcolor = this.bgcolor;
3340         }
3341         if(this.charoff){
3342             cfg.charoff = this.charoff;
3343         }
3344         if(this.valign){
3345             cfg.valign = this.valign;
3346         }
3347         
3348         return cfg;
3349     }
3350    
3351 });
3352
3353  
3354
3355  /*
3356  * - LGPL
3357  *
3358  * table body
3359  * 
3360  */
3361
3362 /**
3363  * @class Roo.bootstrap.TableBody
3364  * @extends Roo.bootstrap.Component
3365  * Bootstrap TableBody class
3366  * @cfg {String} cls element class
3367  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3368  * @cfg {String} align Aligns the content inside the element
3369  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3370  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3371  * 
3372  * @constructor
3373  * Create a new TableBody
3374  * @param {Object} config The config object
3375  */
3376
3377 Roo.bootstrap.TableBody = function(config){
3378     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3379 };
3380
3381 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3382     
3383     cls: false,
3384     tag: false,
3385     align: false,
3386     charoff: false,
3387     valign: false,
3388     
3389     getAutoCreate : function(){
3390         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3391         
3392         cfg = {
3393             tag: 'tbody'
3394         }
3395             
3396         if (this.cls) {
3397             cfg.cls=this.cls
3398         }
3399         if(this.tag){
3400             cfg.tag = this.tag;
3401         }
3402         
3403         if(this.align){
3404             cfg.align = this.align;
3405         }
3406         if(this.charoff){
3407             cfg.charoff = this.charoff;
3408         }
3409         if(this.valign){
3410             cfg.valign = this.valign;
3411         }
3412         
3413         return cfg;
3414     }
3415     
3416     
3417 //    initEvents : function()
3418 //    {
3419 //        
3420 //        if(!this.store){
3421 //            return;
3422 //        }
3423 //        
3424 //        this.store = Roo.factory(this.store, Roo.data);
3425 //        this.store.on('load', this.onLoad, this);
3426 //        
3427 //        this.store.load();
3428 //        
3429 //    },
3430 //    
3431 //    onLoad: function () 
3432 //    {   
3433 //        this.fireEvent('load', this);
3434 //    }
3435 //    
3436 //   
3437 });
3438
3439  
3440
3441  /*
3442  * Based on:
3443  * Ext JS Library 1.1.1
3444  * Copyright(c) 2006-2007, Ext JS, LLC.
3445  *
3446  * Originally Released Under LGPL - original licence link has changed is not relivant.
3447  *
3448  * Fork - LGPL
3449  * <script type="text/javascript">
3450  */
3451
3452 // as we use this in bootstrap.
3453 Roo.namespace('Roo.form');
3454  /**
3455  * @class Roo.form.Action
3456  * Internal Class used to handle form actions
3457  * @constructor
3458  * @param {Roo.form.BasicForm} el The form element or its id
3459  * @param {Object} config Configuration options
3460  */
3461
3462  
3463  
3464 // define the action interface
3465 Roo.form.Action = function(form, options){
3466     this.form = form;
3467     this.options = options || {};
3468 };
3469 /**
3470  * Client Validation Failed
3471  * @const 
3472  */
3473 Roo.form.Action.CLIENT_INVALID = 'client';
3474 /**
3475  * Server Validation Failed
3476  * @const 
3477  */
3478 Roo.form.Action.SERVER_INVALID = 'server';
3479  /**
3480  * Connect to Server Failed
3481  * @const 
3482  */
3483 Roo.form.Action.CONNECT_FAILURE = 'connect';
3484 /**
3485  * Reading Data from Server Failed
3486  * @const 
3487  */
3488 Roo.form.Action.LOAD_FAILURE = 'load';
3489
3490 Roo.form.Action.prototype = {
3491     type : 'default',
3492     failureType : undefined,
3493     response : undefined,
3494     result : undefined,
3495
3496     // interface method
3497     run : function(options){
3498
3499     },
3500
3501     // interface method
3502     success : function(response){
3503
3504     },
3505
3506     // interface method
3507     handleResponse : function(response){
3508
3509     },
3510
3511     // default connection failure
3512     failure : function(response){
3513         
3514         this.response = response;
3515         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3516         this.form.afterAction(this, false);
3517     },
3518
3519     processResponse : function(response){
3520         this.response = response;
3521         if(!response.responseText){
3522             return true;
3523         }
3524         this.result = this.handleResponse(response);
3525         return this.result;
3526     },
3527
3528     // utility functions used internally
3529     getUrl : function(appendParams){
3530         var url = this.options.url || this.form.url || this.form.el.dom.action;
3531         if(appendParams){
3532             var p = this.getParams();
3533             if(p){
3534                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3535             }
3536         }
3537         return url;
3538     },
3539
3540     getMethod : function(){
3541         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3542     },
3543
3544     getParams : function(){
3545         var bp = this.form.baseParams;
3546         var p = this.options.params;
3547         if(p){
3548             if(typeof p == "object"){
3549                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3550             }else if(typeof p == 'string' && bp){
3551                 p += '&' + Roo.urlEncode(bp);
3552             }
3553         }else if(bp){
3554             p = Roo.urlEncode(bp);
3555         }
3556         return p;
3557     },
3558
3559     createCallback : function(){
3560         return {
3561             success: this.success,
3562             failure: this.failure,
3563             scope: this,
3564             timeout: (this.form.timeout*1000),
3565             upload: this.form.fileUpload ? this.success : undefined
3566         };
3567     }
3568 };
3569
3570 Roo.form.Action.Submit = function(form, options){
3571     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3572 };
3573
3574 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3575     type : 'submit',
3576
3577     haveProgress : false,
3578     uploadComplete : false,
3579     
3580     // uploadProgress indicator.
3581     uploadProgress : function()
3582     {
3583         if (!this.form.progressUrl) {
3584             return;
3585         }
3586         
3587         if (!this.haveProgress) {
3588             Roo.MessageBox.progress("Uploading", "Uploading");
3589         }
3590         if (this.uploadComplete) {
3591            Roo.MessageBox.hide();
3592            return;
3593         }
3594         
3595         this.haveProgress = true;
3596    
3597         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3598         
3599         var c = new Roo.data.Connection();
3600         c.request({
3601             url : this.form.progressUrl,
3602             params: {
3603                 id : uid
3604             },
3605             method: 'GET',
3606             success : function(req){
3607                //console.log(data);
3608                 var rdata = false;
3609                 var edata;
3610                 try  {
3611                    rdata = Roo.decode(req.responseText)
3612                 } catch (e) {
3613                     Roo.log("Invalid data from server..");
3614                     Roo.log(edata);
3615                     return;
3616                 }
3617                 if (!rdata || !rdata.success) {
3618                     Roo.log(rdata);
3619                     Roo.MessageBox.alert(Roo.encode(rdata));
3620                     return;
3621                 }
3622                 var data = rdata.data;
3623                 
3624                 if (this.uploadComplete) {
3625                    Roo.MessageBox.hide();
3626                    return;
3627                 }
3628                    
3629                 if (data){
3630                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3631                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3632                     );
3633                 }
3634                 this.uploadProgress.defer(2000,this);
3635             },
3636        
3637             failure: function(data) {
3638                 Roo.log('progress url failed ');
3639                 Roo.log(data);
3640             },
3641             scope : this
3642         });
3643            
3644     },
3645     
3646     
3647     run : function()
3648     {
3649         // run get Values on the form, so it syncs any secondary forms.
3650         this.form.getValues();
3651         
3652         var o = this.options;
3653         var method = this.getMethod();
3654         var isPost = method == 'POST';
3655         if(o.clientValidation === false || this.form.isValid()){
3656             
3657             if (this.form.progressUrl) {
3658                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3659                     (new Date() * 1) + '' + Math.random());
3660                     
3661             } 
3662             
3663             
3664             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3665                 form:this.form.el.dom,
3666                 url:this.getUrl(!isPost),
3667                 method: method,
3668                 params:isPost ? this.getParams() : null,
3669                 isUpload: this.form.fileUpload
3670             }));
3671             
3672             this.uploadProgress();
3673
3674         }else if (o.clientValidation !== false){ // client validation failed
3675             this.failureType = Roo.form.Action.CLIENT_INVALID;
3676             this.form.afterAction(this, false);
3677         }
3678     },
3679
3680     success : function(response)
3681     {
3682         this.uploadComplete= true;
3683         if (this.haveProgress) {
3684             Roo.MessageBox.hide();
3685         }
3686         
3687         
3688         var result = this.processResponse(response);
3689         if(result === true || result.success){
3690             this.form.afterAction(this, true);
3691             return;
3692         }
3693         if(result.errors){
3694             this.form.markInvalid(result.errors);
3695             this.failureType = Roo.form.Action.SERVER_INVALID;
3696         }
3697         this.form.afterAction(this, false);
3698     },
3699     failure : function(response)
3700     {
3701         this.uploadComplete= true;
3702         if (this.haveProgress) {
3703             Roo.MessageBox.hide();
3704         }
3705         
3706         this.response = response;
3707         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3708         this.form.afterAction(this, false);
3709     },
3710     
3711     handleResponse : function(response){
3712         if(this.form.errorReader){
3713             var rs = this.form.errorReader.read(response);
3714             var errors = [];
3715             if(rs.records){
3716                 for(var i = 0, len = rs.records.length; i < len; i++) {
3717                     var r = rs.records[i];
3718                     errors[i] = r.data;
3719                 }
3720             }
3721             if(errors.length < 1){
3722                 errors = null;
3723             }
3724             return {
3725                 success : rs.success,
3726                 errors : errors
3727             };
3728         }
3729         var ret = false;
3730         try {
3731             ret = Roo.decode(response.responseText);
3732         } catch (e) {
3733             ret = {
3734                 success: false,
3735                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3736                 errors : []
3737             };
3738         }
3739         return ret;
3740         
3741     }
3742 });
3743
3744
3745 Roo.form.Action.Load = function(form, options){
3746     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3747     this.reader = this.form.reader;
3748 };
3749
3750 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3751     type : 'load',
3752
3753     run : function(){
3754         
3755         Roo.Ajax.request(Roo.apply(
3756                 this.createCallback(), {
3757                     method:this.getMethod(),
3758                     url:this.getUrl(false),
3759                     params:this.getParams()
3760         }));
3761     },
3762
3763     success : function(response){
3764         
3765         var result = this.processResponse(response);
3766         if(result === true || !result.success || !result.data){
3767             this.failureType = Roo.form.Action.LOAD_FAILURE;
3768             this.form.afterAction(this, false);
3769             return;
3770         }
3771         this.form.clearInvalid();
3772         this.form.setValues(result.data);
3773         this.form.afterAction(this, true);
3774     },
3775
3776     handleResponse : function(response){
3777         if(this.form.reader){
3778             var rs = this.form.reader.read(response);
3779             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3780             return {
3781                 success : rs.success,
3782                 data : data
3783             };
3784         }
3785         return Roo.decode(response.responseText);
3786     }
3787 });
3788
3789 Roo.form.Action.ACTION_TYPES = {
3790     'load' : Roo.form.Action.Load,
3791     'submit' : Roo.form.Action.Submit
3792 };/*
3793  * - LGPL
3794  *
3795  * form
3796  * 
3797  */
3798
3799 /**
3800  * @class Roo.bootstrap.Form
3801  * @extends Roo.bootstrap.Component
3802  * Bootstrap Form class
3803  * @cfg {String} method  GET | POST (default POST)
3804  * @cfg {String} labelAlign top | left (default top)
3805   * @cfg {String} align left  | right - for navbars
3806
3807  * 
3808  * @constructor
3809  * Create a new Form
3810  * @param {Object} config The config object
3811  */
3812
3813
3814 Roo.bootstrap.Form = function(config){
3815     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3816     this.addEvents({
3817         /**
3818          * @event clientvalidation
3819          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3820          * @param {Form} this
3821          * @param {Boolean} valid true if the form has passed client-side validation
3822          */
3823         clientvalidation: true,
3824         /**
3825          * @event beforeaction
3826          * Fires before any action is performed. Return false to cancel the action.
3827          * @param {Form} this
3828          * @param {Action} action The action to be performed
3829          */
3830         beforeaction: true,
3831         /**
3832          * @event actionfailed
3833          * Fires when an action fails.
3834          * @param {Form} this
3835          * @param {Action} action The action that failed
3836          */
3837         actionfailed : true,
3838         /**
3839          * @event actioncomplete
3840          * Fires when an action is completed.
3841          * @param {Form} this
3842          * @param {Action} action The action that completed
3843          */
3844         actioncomplete : true
3845     });
3846     
3847 };
3848
3849 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3850       
3851      /**
3852      * @cfg {String} method
3853      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3854      */
3855     method : 'POST',
3856     /**
3857      * @cfg {String} url
3858      * The URL to use for form actions if one isn't supplied in the action options.
3859      */
3860     /**
3861      * @cfg {Boolean} fileUpload
3862      * Set to true if this form is a file upload.
3863      */
3864      
3865     /**
3866      * @cfg {Object} baseParams
3867      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3868      */
3869       
3870     /**
3871      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3872      */
3873     timeout: 30,
3874     /**
3875      * @cfg {Sting} align (left|right) for navbar forms
3876      */
3877     align : 'left',
3878
3879     // private
3880     activeAction : null,
3881  
3882     /**
3883      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3884      * element by passing it or its id or mask the form itself by passing in true.
3885      * @type Mixed
3886      */
3887     waitMsgTarget : false,
3888     
3889      
3890     
3891     /**
3892      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3893      * element by passing it or its id or mask the form itself by passing in true.
3894      * @type Mixed
3895      */
3896     
3897     getAutoCreate : function(){
3898         
3899         var cfg = {
3900             tag: 'form',
3901             method : this.method || 'POST',
3902             id : this.id || Roo.id(),
3903             cls : ''
3904         }
3905         if (this.parent().xtype.match(/^Nav/)) {
3906             cfg.cls = 'navbar-form navbar-' + this.align;
3907             
3908         }
3909         
3910         if (this.labelAlign == 'left' ) {
3911             cfg.cls += ' form-horizontal';
3912         }
3913         
3914         
3915         return cfg;
3916     },
3917     initEvents : function()
3918     {
3919         this.el.on('submit', this.onSubmit, this);
3920         
3921         
3922     },
3923     // private
3924     onSubmit : function(e){
3925         e.stopEvent();
3926     },
3927     
3928      /**
3929      * Returns true if client-side validation on the form is successful.
3930      * @return Boolean
3931      */
3932     isValid : function(){
3933         var items = this.getItems();
3934         var valid = true;
3935         items.each(function(f){
3936            if(!f.validate()){
3937                valid = false;
3938                
3939            }
3940         });
3941         return valid;
3942     },
3943     /**
3944      * Returns true if any fields in this form have changed since their original load.
3945      * @return Boolean
3946      */
3947     isDirty : function(){
3948         var dirty = false;
3949         var items = this.getItems();
3950         items.each(function(f){
3951            if(f.isDirty()){
3952                dirty = true;
3953                return false;
3954            }
3955            return true;
3956         });
3957         return dirty;
3958     },
3959      /**
3960      * Performs a predefined action (submit or load) or custom actions you define on this form.
3961      * @param {String} actionName The name of the action type
3962      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3963      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3964      * accept other config options):
3965      * <pre>
3966 Property          Type             Description
3967 ----------------  ---------------  ----------------------------------------------------------------------------------
3968 url               String           The url for the action (defaults to the form's url)
3969 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3970 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3971 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3972                                    validate the form on the client (defaults to false)
3973      * </pre>
3974      * @return {BasicForm} this
3975      */
3976     doAction : function(action, options){
3977         if(typeof action == 'string'){
3978             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3979         }
3980         if(this.fireEvent('beforeaction', this, action) !== false){
3981             this.beforeAction(action);
3982             action.run.defer(100, action);
3983         }
3984         return this;
3985     },
3986     
3987     // private
3988     beforeAction : function(action){
3989         var o = action.options;
3990         
3991         // not really supported yet.. ??
3992         
3993         //if(this.waitMsgTarget === true){
3994             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3995         //}else if(this.waitMsgTarget){
3996         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3997         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3998         //}else {
3999         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4000        // }
4001          
4002     },
4003
4004     // private
4005     afterAction : function(action, success){
4006         this.activeAction = null;
4007         var o = action.options;
4008         
4009         //if(this.waitMsgTarget === true){
4010             this.el.unmask();
4011         //}else if(this.waitMsgTarget){
4012         //    this.waitMsgTarget.unmask();
4013         //}else{
4014         //    Roo.MessageBox.updateProgress(1);
4015         //    Roo.MessageBox.hide();
4016        // }
4017         // 
4018         if(success){
4019             if(o.reset){
4020                 this.reset();
4021             }
4022             Roo.callback(o.success, o.scope, [this, action]);
4023             this.fireEvent('actioncomplete', this, action);
4024             
4025         }else{
4026             
4027             // failure condition..
4028             // we have a scenario where updates need confirming.
4029             // eg. if a locking scenario exists..
4030             // we look for { errors : { needs_confirm : true }} in the response.
4031             if (
4032                 (typeof(action.result) != 'undefined')  &&
4033                 (typeof(action.result.errors) != 'undefined')  &&
4034                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4035            ){
4036                 var _t = this;
4037                 Roo.log("not supported yet");
4038                  /*
4039                 
4040                 Roo.MessageBox.confirm(
4041                     "Change requires confirmation",
4042                     action.result.errorMsg,
4043                     function(r) {
4044                         if (r != 'yes') {
4045                             return;
4046                         }
4047                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4048                     }
4049                     
4050                 );
4051                 */
4052                 
4053                 
4054                 return;
4055             }
4056             
4057             Roo.callback(o.failure, o.scope, [this, action]);
4058             // show an error message if no failed handler is set..
4059             if (!this.hasListener('actionfailed')) {
4060                 Roo.log("need to add dialog support");
4061                 /*
4062                 Roo.MessageBox.alert("Error",
4063                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4064                         action.result.errorMsg :
4065                         "Saving Failed, please check your entries or try again"
4066                 );
4067                 */
4068             }
4069             
4070             this.fireEvent('actionfailed', this, action);
4071         }
4072         
4073     },
4074     /**
4075      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4076      * @param {String} id The value to search for
4077      * @return Field
4078      */
4079     findField : function(id){
4080         var items = this.getItems();
4081         var field = items.get(id);
4082         if(!field){
4083              items.each(function(f){
4084                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4085                     field = f;
4086                     return false;
4087                 }
4088                 return true;
4089             });
4090         }
4091         return field || null;
4092     },
4093      /**
4094      * Mark fields in this form invalid in bulk.
4095      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4096      * @return {BasicForm} this
4097      */
4098     markInvalid : function(errors){
4099         if(errors instanceof Array){
4100             for(var i = 0, len = errors.length; i < len; i++){
4101                 var fieldError = errors[i];
4102                 var f = this.findField(fieldError.id);
4103                 if(f){
4104                     f.markInvalid(fieldError.msg);
4105                 }
4106             }
4107         }else{
4108             var field, id;
4109             for(id in errors){
4110                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4111                     field.markInvalid(errors[id]);
4112                 }
4113             }
4114         }
4115         //Roo.each(this.childForms || [], function (f) {
4116         //    f.markInvalid(errors);
4117         //});
4118         
4119         return this;
4120     },
4121
4122     /**
4123      * Set values for fields in this form in bulk.
4124      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4125      * @return {BasicForm} this
4126      */
4127     setValues : function(values){
4128         if(values instanceof Array){ // array of objects
4129             for(var i = 0, len = values.length; i < len; i++){
4130                 var v = values[i];
4131                 var f = this.findField(v.id);
4132                 if(f){
4133                     f.setValue(v.value);
4134                     if(this.trackResetOnLoad){
4135                         f.originalValue = f.getValue();
4136                     }
4137                 }
4138             }
4139         }else{ // object hash
4140             var field, id;
4141             for(id in values){
4142                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4143                     
4144                     if (field.setFromData && 
4145                         field.valueField && 
4146                         field.displayField &&
4147                         // combos' with local stores can 
4148                         // be queried via setValue()
4149                         // to set their value..
4150                         (field.store && !field.store.isLocal)
4151                         ) {
4152                         // it's a combo
4153                         var sd = { };
4154                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4155                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4156                         field.setFromData(sd);
4157                         
4158                     } else {
4159                         field.setValue(values[id]);
4160                     }
4161                     
4162                     
4163                     if(this.trackResetOnLoad){
4164                         field.originalValue = field.getValue();
4165                     }
4166                 }
4167             }
4168         }
4169          
4170         //Roo.each(this.childForms || [], function (f) {
4171         //    f.setValues(values);
4172         //});
4173                 
4174         return this;
4175     },
4176
4177     /**
4178      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4179      * they are returned as an array.
4180      * @param {Boolean} asString
4181      * @return {Object}
4182      */
4183     getValues : function(asString){
4184         //if (this.childForms) {
4185             // copy values from the child forms
4186         //    Roo.each(this.childForms, function (f) {
4187         //        this.setValues(f.getValues());
4188         //    }, this);
4189         //}
4190         
4191         
4192         
4193         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4194         if(asString === true){
4195             return fs;
4196         }
4197         return Roo.urlDecode(fs);
4198     },
4199     
4200     /**
4201      * Returns the fields in this form as an object with key/value pairs. 
4202      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4203      * @return {Object}
4204      */
4205     getFieldValues : function(with_hidden)
4206     {
4207         var items = this.getItems();
4208         var ret = {};
4209         items.each(function(f){
4210             if (!f.getName()) {
4211                 return;
4212             }
4213             var v = f.getValue();
4214             if (f.inputType =='radio') {
4215                 if (typeof(ret[f.getName()]) == 'undefined') {
4216                     ret[f.getName()] = ''; // empty..
4217                 }
4218                 
4219                 if (!f.el.dom.checked) {
4220                     return;
4221                     
4222                 }
4223                 v = f.el.dom.value;
4224                 
4225             }
4226             
4227             // not sure if this supported any more..
4228             if ((typeof(v) == 'object') && f.getRawValue) {
4229                 v = f.getRawValue() ; // dates..
4230             }
4231             // combo boxes where name != hiddenName...
4232             if (f.name != f.getName()) {
4233                 ret[f.name] = f.getRawValue();
4234             }
4235             ret[f.getName()] = v;
4236         });
4237         
4238         return ret;
4239     },
4240
4241     /**
4242      * Clears all invalid messages in this form.
4243      * @return {BasicForm} this
4244      */
4245     clearInvalid : function(){
4246         var items = this.getItems();
4247         
4248         items.each(function(f){
4249            f.clearInvalid();
4250         });
4251         
4252         
4253         
4254         return this;
4255     },
4256
4257     /**
4258      * Resets this form.
4259      * @return {BasicForm} this
4260      */
4261     reset : function(){
4262         var items = this.getItems();
4263         items.each(function(f){
4264             f.reset();
4265         });
4266         
4267         Roo.each(this.childForms || [], function (f) {
4268             f.reset();
4269         });
4270        
4271         
4272         return this;
4273     },
4274     getItems : function()
4275     {
4276         var r=new Roo.util.MixedCollection(false, function(o){
4277             return o.id || (o.id = Roo.id());
4278         });
4279         var iter = function(el) {
4280             if (el.inputEl) {
4281                 r.add(el);
4282             }
4283             if (!el.items) {
4284                 return;
4285             }
4286             Roo.each(el.items,function(e) {
4287                 iter(e);
4288             });
4289             
4290             
4291         };
4292         iter(this);
4293         return r;
4294         
4295         
4296         
4297         
4298     }
4299     
4300 });
4301
4302  
4303 /*
4304  * Based on:
4305  * Ext JS Library 1.1.1
4306  * Copyright(c) 2006-2007, Ext JS, LLC.
4307  *
4308  * Originally Released Under LGPL - original licence link has changed is not relivant.
4309  *
4310  * Fork - LGPL
4311  * <script type="text/javascript">
4312  */
4313 /**
4314  * @class Roo.form.VTypes
4315  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4316  * @singleton
4317  */
4318 Roo.form.VTypes = function(){
4319     // closure these in so they are only created once.
4320     var alpha = /^[a-zA-Z_]+$/;
4321     var alphanum = /^[a-zA-Z0-9_]+$/;
4322     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4323     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4324
4325     // All these messages and functions are configurable
4326     return {
4327         /**
4328          * The function used to validate email addresses
4329          * @param {String} value The email address
4330          */
4331         'email' : function(v){
4332             return email.test(v);
4333         },
4334         /**
4335          * The error text to display when the email validation function returns false
4336          * @type String
4337          */
4338         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4339         /**
4340          * The keystroke filter mask to be applied on email input
4341          * @type RegExp
4342          */
4343         'emailMask' : /[a-z0-9_\.\-@]/i,
4344
4345         /**
4346          * The function used to validate URLs
4347          * @param {String} value The URL
4348          */
4349         'url' : function(v){
4350             return url.test(v);
4351         },
4352         /**
4353          * The error text to display when the url validation function returns false
4354          * @type String
4355          */
4356         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4357         
4358         /**
4359          * The function used to validate alpha values
4360          * @param {String} value The value
4361          */
4362         'alpha' : function(v){
4363             return alpha.test(v);
4364         },
4365         /**
4366          * The error text to display when the alpha validation function returns false
4367          * @type String
4368          */
4369         'alphaText' : 'This field should only contain letters and _',
4370         /**
4371          * The keystroke filter mask to be applied on alpha input
4372          * @type RegExp
4373          */
4374         'alphaMask' : /[a-z_]/i,
4375
4376         /**
4377          * The function used to validate alphanumeric values
4378          * @param {String} value The value
4379          */
4380         'alphanum' : function(v){
4381             return alphanum.test(v);
4382         },
4383         /**
4384          * The error text to display when the alphanumeric validation function returns false
4385          * @type String
4386          */
4387         'alphanumText' : 'This field should only contain letters, numbers and _',
4388         /**
4389          * The keystroke filter mask to be applied on alphanumeric input
4390          * @type RegExp
4391          */
4392         'alphanumMask' : /[a-z0-9_]/i
4393     };
4394 }();/*
4395  * - LGPL
4396  *
4397  * Input
4398  * 
4399  */
4400
4401 /**
4402  * @class Roo.bootstrap.Input
4403  * @extends Roo.bootstrap.Component
4404  * Bootstrap Input class
4405  * @cfg {Boolean} disabled is it disabled
4406  * @cfg {String} fieldLabel - the label associated
4407  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4408  * @cfg {String} name name of the input
4409  * @cfg {string} fieldLabel - the label associated
4410  * @cfg {string}  inputType - input / file submit ...
4411  * @cfg {string} placeholder - placeholder to put in text.
4412  * @cfg {string}  before - input group add on before
4413  * @cfg {string} after - input group add on after
4414  * @cfg {string} size - (lg|sm) or leave empty..
4415  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4416  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4417  * @cfg {Number} md colspan out of 12 for computer-sized screens
4418  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4419  * @cfg {string} value default value of the input
4420  * @cfg {Number} labelWidth set the width of label (0-12)
4421  * @cfg {String} labelAlign (top|left)
4422  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4423  * 
4424  * 
4425  * @constructor
4426  * Create a new Input
4427  * @param {Object} config The config object
4428  */
4429
4430 Roo.bootstrap.Input = function(config){
4431     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4432    
4433         this.addEvents({
4434             /**
4435              * @event focus
4436              * Fires when this field receives input focus.
4437              * @param {Roo.form.Field} this
4438              */
4439             focus : true,
4440             /**
4441              * @event blur
4442              * Fires when this field loses input focus.
4443              * @param {Roo.form.Field} this
4444              */
4445             blur : true,
4446             /**
4447              * @event specialkey
4448              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4449              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4450              * @param {Roo.form.Field} this
4451              * @param {Roo.EventObject} e The event object
4452              */
4453             specialkey : true,
4454             /**
4455              * @event change
4456              * Fires just before the field blurs if the field value has changed.
4457              * @param {Roo.form.Field} this
4458              * @param {Mixed} newValue The new value
4459              * @param {Mixed} oldValue The original value
4460              */
4461             change : true,
4462             /**
4463              * @event invalid
4464              * Fires after the field has been marked as invalid.
4465              * @param {Roo.form.Field} this
4466              * @param {String} msg The validation message
4467              */
4468             invalid : true,
4469             /**
4470              * @event valid
4471              * Fires after the field has been validated with no errors.
4472              * @param {Roo.form.Field} this
4473              */
4474             valid : true,
4475              /**
4476              * @event keyup
4477              * Fires after the key up
4478              * @param {Roo.form.Field} this
4479              * @param {Roo.EventObject}  e The event Object
4480              */
4481             keyup : true
4482         });
4483 };
4484
4485 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4486      /**
4487      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4488       automatic validation (defaults to "keyup").
4489      */
4490     validationEvent : "keyup",
4491      /**
4492      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4493      */
4494     validateOnBlur : true,
4495     /**
4496      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4497      */
4498     validationDelay : 250,
4499      /**
4500      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4501      */
4502     focusClass : "x-form-focus",  // not needed???
4503     
4504        
4505     /**
4506      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4507      */
4508     invalidClass : "has-error",
4509     
4510     /**
4511      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4512      */
4513     selectOnFocus : false,
4514     
4515      /**
4516      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4517      */
4518     maskRe : null,
4519        /**
4520      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4521      */
4522     vtype : null,
4523     
4524       /**
4525      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4526      */
4527     disableKeyFilter : false,
4528     
4529        /**
4530      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4531      */
4532     disabled : false,
4533      /**
4534      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4535      */
4536     allowBlank : true,
4537     /**
4538      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4539      */
4540     blankText : "This field is required",
4541     
4542      /**
4543      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4544      */
4545     minLength : 0,
4546     /**
4547      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4548      */
4549     maxLength : Number.MAX_VALUE,
4550     /**
4551      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4552      */
4553     minLengthText : "The minimum length for this field is {0}",
4554     /**
4555      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4556      */
4557     maxLengthText : "The maximum length for this field is {0}",
4558   
4559     
4560     /**
4561      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4562      * If available, this function will be called only after the basic validators all return true, and will be passed the
4563      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4564      */
4565     validator : null,
4566     /**
4567      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4568      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4569      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4570      */
4571     regex : null,
4572     /**
4573      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4574      */
4575     regexText : "",
4576     
4577     
4578     
4579     fieldLabel : '',
4580     inputType : 'text',
4581     
4582     name : false,
4583     placeholder: false,
4584     before : false,
4585     after : false,
4586     size : false,
4587     // private
4588     hasFocus : false,
4589     preventMark: false,
4590     isFormField : true,
4591     value : '',
4592     labelWidth : 2,
4593     labelAlign : false,
4594     readOnly : false,
4595     
4596     parentLabelAlign : function()
4597     {
4598         var parent = this;
4599         while (parent.parent()) {
4600             parent = parent.parent();
4601             if (typeof(parent.labelAlign) !='undefined') {
4602                 return parent.labelAlign;
4603             }
4604         }
4605         return 'left';
4606         
4607     },
4608     
4609     getAutoCreate : function(){
4610         
4611         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4612         
4613         var id = Roo.id();
4614         
4615         var cfg = {};
4616         
4617         if(this.inputType != 'hidden'){
4618             cfg.cls = 'form-group' //input-group
4619         }
4620         
4621         var input =  {
4622             tag: 'input',
4623             id : id,
4624             type : this.inputType,
4625             value : this.value,
4626             cls : 'form-control',
4627             placeholder : this.placeholder || ''
4628             
4629         };
4630         
4631         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4632             input.maxLength = this.maxLength;
4633         }
4634         
4635         if (this.disabled) {
4636             input.disabled=true;
4637         }
4638         
4639         if (this.readOnly) {
4640             input.readonly=true;
4641         }
4642         
4643         if (this.name) {
4644             input.name = this.name;
4645         }
4646         if (this.size) {
4647             input.cls += ' input-' + this.size;
4648         }
4649         var settings=this;
4650         ['xs','sm','md','lg'].map(function(size){
4651             if (settings[size]) {
4652                 cfg.cls += ' col-' + size + '-' + settings[size];
4653             }
4654         });
4655         
4656         var inputblock = input;
4657         
4658         if (this.before || this.after) {
4659             
4660             inputblock = {
4661                 cls : 'input-group',
4662                 cn :  [] 
4663             };
4664             if (this.before) {
4665                 inputblock.cn.push({
4666                     tag :'span',
4667                     cls : 'input-group-addon',
4668                     html : this.before
4669                 });
4670             }
4671             inputblock.cn.push(input);
4672             if (this.after) {
4673                 inputblock.cn.push({
4674                     tag :'span',
4675                     cls : 'input-group-addon',
4676                     html : this.after
4677                 });
4678             }
4679             
4680         };
4681         
4682         if (align ==='left' && this.fieldLabel.length) {
4683                 Roo.log("left and has label");
4684                 cfg.cn = [
4685                     
4686                     {
4687                         tag: 'label',
4688                         'for' :  id,
4689                         cls : 'control-label col-sm-' + this.labelWidth,
4690                         html : this.fieldLabel
4691                         
4692                     },
4693                     {
4694                         cls : "col-sm-" + (12 - this.labelWidth), 
4695                         cn: [
4696                             inputblock
4697                         ]
4698                     }
4699                     
4700                 ];
4701         } else if ( this.fieldLabel.length) {
4702                 Roo.log(" label");
4703                  cfg.cn = [
4704                    
4705                     {
4706                         tag: 'label',
4707                         //cls : 'input-group-addon',
4708                         html : this.fieldLabel
4709                         
4710                     },
4711                     
4712                     inputblock
4713                     
4714                 ];
4715
4716         } else {
4717             
4718                 Roo.log(" no label && no align");
4719                 cfg.cn = [
4720                     
4721                         inputblock
4722                     
4723                 ];
4724                 
4725                 
4726         };
4727         Roo.log('input-parentType: ' + this.parentType);
4728         
4729         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4730            cfg.cls += ' navbar-form';
4731            Roo.log(cfg);
4732         }
4733         
4734         return cfg;
4735         
4736     },
4737     /**
4738      * return the real input element.
4739      */
4740     inputEl: function ()
4741     {
4742         return this.el.select('input.form-control',true).first();
4743     },
4744     setDisabled : function(v)
4745     {
4746         var i  = this.inputEl().dom;
4747         if (!v) {
4748             i.removeAttribute('disabled');
4749             return;
4750             
4751         }
4752         i.setAttribute('disabled','true');
4753     },
4754     initEvents : function()
4755     {
4756         
4757         this.inputEl().on("keydown" , this.fireKey,  this);
4758         this.inputEl().on("focus", this.onFocus,  this);
4759         this.inputEl().on("blur", this.onBlur,  this);
4760         
4761         this.inputEl().relayEvent('keyup', this);
4762
4763         // reference to original value for reset
4764         this.originalValue = this.getValue();
4765         //Roo.form.TextField.superclass.initEvents.call(this);
4766         if(this.validationEvent == 'keyup'){
4767             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4768             this.inputEl().on('keyup', this.filterValidation, this);
4769         }
4770         else if(this.validationEvent !== false){
4771             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4772         }
4773         
4774         if(this.selectOnFocus){
4775             this.on("focus", this.preFocus, this);
4776             
4777         }
4778         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4779             this.inputEl().on("keypress", this.filterKeys, this);
4780         }
4781        /* if(this.grow){
4782             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4783             this.el.on("click", this.autoSize,  this);
4784         }
4785         */
4786         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4787             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4788         }
4789         
4790     },
4791     filterValidation : function(e){
4792         if(!e.isNavKeyPress()){
4793             this.validationTask.delay(this.validationDelay);
4794         }
4795     },
4796      /**
4797      * Validates the field value
4798      * @return {Boolean} True if the value is valid, else false
4799      */
4800     validate : function(){
4801         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4802         if(this.disabled || this.validateValue(this.getRawValue())){
4803             this.clearInvalid();
4804             return true;
4805         }
4806         return false;
4807     },
4808     
4809     
4810     /**
4811      * Validates a value according to the field's validation rules and marks the field as invalid
4812      * if the validation fails
4813      * @param {Mixed} value The value to validate
4814      * @return {Boolean} True if the value is valid, else false
4815      */
4816     validateValue : function(value){
4817         if(value.length < 1)  { // if it's blank
4818              if(this.allowBlank){
4819                 this.clearInvalid();
4820                 return true;
4821              }else{
4822                 this.markInvalid(this.blankText);
4823                 return false;
4824              }
4825         }
4826         if(value.length < this.minLength){
4827             this.markInvalid(String.format(this.minLengthText, this.minLength));
4828             return false;
4829         }
4830         if(value.length > this.maxLength){
4831             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4832             return false;
4833         }
4834         if(this.vtype){
4835             var vt = Roo.form.VTypes;
4836             if(!vt[this.vtype](value, this)){
4837                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4838                 return false;
4839             }
4840         }
4841         if(typeof this.validator == "function"){
4842             var msg = this.validator(value);
4843             if(msg !== true){
4844                 this.markInvalid(msg);
4845                 return false;
4846             }
4847         }
4848         if(this.regex && !this.regex.test(value)){
4849             this.markInvalid(this.regexText);
4850             return false;
4851         }
4852         return true;
4853     },
4854
4855     
4856     
4857      // private
4858     fireKey : function(e){
4859         //Roo.log('field ' + e.getKey());
4860         if(e.isNavKeyPress()){
4861             this.fireEvent("specialkey", this, e);
4862         }
4863     },
4864     focus : function (selectText){
4865         if(this.rendered){
4866             this.inputEl().focus();
4867             if(selectText === true){
4868                 this.inputEl().dom.select();
4869             }
4870         }
4871         return this;
4872     } ,
4873     
4874     onFocus : function(){
4875         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4876            // this.el.addClass(this.focusClass);
4877         }
4878         if(!this.hasFocus){
4879             this.hasFocus = true;
4880             this.startValue = this.getValue();
4881             this.fireEvent("focus", this);
4882         }
4883     },
4884     
4885     beforeBlur : Roo.emptyFn,
4886
4887     
4888     // private
4889     onBlur : function(){
4890         this.beforeBlur();
4891         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4892             //this.el.removeClass(this.focusClass);
4893         }
4894         this.hasFocus = false;
4895         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4896             this.validate();
4897         }
4898         var v = this.getValue();
4899         if(String(v) !== String(this.startValue)){
4900             this.fireEvent('change', this, v, this.startValue);
4901         }
4902         this.fireEvent("blur", this);
4903     },
4904     
4905     /**
4906      * Resets the current field value to the originally loaded value and clears any validation messages
4907      */
4908     reset : function(){
4909         this.setValue(this.originalValue);
4910         this.clearInvalid();
4911     },
4912      /**
4913      * Returns the name of the field
4914      * @return {Mixed} name The name field
4915      */
4916     getName: function(){
4917         return this.name;
4918     },
4919      /**
4920      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4921      * @return {Mixed} value The field value
4922      */
4923     getValue : function(){
4924         return this.inputEl().getValue();
4925     },
4926     /**
4927      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4928      * @return {Mixed} value The field value
4929      */
4930     getRawValue : function(){
4931         var v = this.inputEl().getValue();
4932         
4933         return v;
4934     },
4935     
4936     /**
4937      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4938      * @param {Mixed} value The value to set
4939      */
4940     setRawValue : function(v){
4941         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4942     },
4943     
4944     selectText : function(start, end){
4945         var v = this.getRawValue();
4946         if(v.length > 0){
4947             start = start === undefined ? 0 : start;
4948             end = end === undefined ? v.length : end;
4949             var d = this.inputEl().dom;
4950             if(d.setSelectionRange){
4951                 d.setSelectionRange(start, end);
4952             }else if(d.createTextRange){
4953                 var range = d.createTextRange();
4954                 range.moveStart("character", start);
4955                 range.moveEnd("character", v.length-end);
4956                 range.select();
4957             }
4958         }
4959     },
4960     
4961     /**
4962      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4963      * @param {Mixed} value The value to set
4964      */
4965     setValue : function(v){
4966         this.value = v;
4967         if(this.rendered){
4968             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4969             this.validate();
4970         }
4971     },
4972     
4973     /*
4974     processValue : function(value){
4975         if(this.stripCharsRe){
4976             var newValue = value.replace(this.stripCharsRe, '');
4977             if(newValue !== value){
4978                 this.setRawValue(newValue);
4979                 return newValue;
4980             }
4981         }
4982         return value;
4983     },
4984   */
4985     preFocus : function(){
4986         
4987         if(this.selectOnFocus){
4988             this.inputEl().dom.select();
4989         }
4990     },
4991     filterKeys : function(e){
4992         var k = e.getKey();
4993         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4994             return;
4995         }
4996         var c = e.getCharCode(), cc = String.fromCharCode(c);
4997         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4998             return;
4999         }
5000         if(!this.maskRe.test(cc)){
5001             e.stopEvent();
5002         }
5003     },
5004      /**
5005      * Clear any invalid styles/messages for this field
5006      */
5007     clearInvalid : function(){
5008         
5009         if(!this.el || this.preventMark){ // not rendered
5010             return;
5011         }
5012         this.el.removeClass(this.invalidClass);
5013         /*
5014         switch(this.msgTarget){
5015             case 'qtip':
5016                 this.el.dom.qtip = '';
5017                 break;
5018             case 'title':
5019                 this.el.dom.title = '';
5020                 break;
5021             case 'under':
5022                 if(this.errorEl){
5023                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5024                 }
5025                 break;
5026             case 'side':
5027                 if(this.errorIcon){
5028                     this.errorIcon.dom.qtip = '';
5029                     this.errorIcon.hide();
5030                     this.un('resize', this.alignErrorIcon, this);
5031                 }
5032                 break;
5033             default:
5034                 var t = Roo.getDom(this.msgTarget);
5035                 t.innerHTML = '';
5036                 t.style.display = 'none';
5037                 break;
5038         }
5039         */
5040         this.fireEvent('valid', this);
5041     },
5042      /**
5043      * Mark this field as invalid
5044      * @param {String} msg The validation message
5045      */
5046     markInvalid : function(msg){
5047         if(!this.el  || this.preventMark){ // not rendered
5048             return;
5049         }
5050         this.el.addClass(this.invalidClass);
5051         /*
5052         msg = msg || this.invalidText;
5053         switch(this.msgTarget){
5054             case 'qtip':
5055                 this.el.dom.qtip = msg;
5056                 this.el.dom.qclass = 'x-form-invalid-tip';
5057                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5058                     Roo.QuickTips.enable();
5059                 }
5060                 break;
5061             case 'title':
5062                 this.el.dom.title = msg;
5063                 break;
5064             case 'under':
5065                 if(!this.errorEl){
5066                     var elp = this.el.findParent('.x-form-element', 5, true);
5067                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5068                     this.errorEl.setWidth(elp.getWidth(true)-20);
5069                 }
5070                 this.errorEl.update(msg);
5071                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5072                 break;
5073             case 'side':
5074                 if(!this.errorIcon){
5075                     var elp = this.el.findParent('.x-form-element', 5, true);
5076                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5077                 }
5078                 this.alignErrorIcon();
5079                 this.errorIcon.dom.qtip = msg;
5080                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5081                 this.errorIcon.show();
5082                 this.on('resize', this.alignErrorIcon, this);
5083                 break;
5084             default:
5085                 var t = Roo.getDom(this.msgTarget);
5086                 t.innerHTML = msg;
5087                 t.style.display = this.msgDisplay;
5088                 break;
5089         }
5090         */
5091         this.fireEvent('invalid', this, msg);
5092     },
5093     // private
5094     SafariOnKeyDown : function(event)
5095     {
5096         // this is a workaround for a password hang bug on chrome/ webkit.
5097         
5098         var isSelectAll = false;
5099         
5100         if(this.inputEl().dom.selectionEnd > 0){
5101             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5102         }
5103         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5104             event.preventDefault();
5105             this.setValue('');
5106             return;
5107         }
5108         
5109         if(isSelectAll){ // backspace and delete key
5110             
5111             event.preventDefault();
5112             // this is very hacky as keydown always get's upper case.
5113             //
5114             var cc = String.fromCharCode(event.getCharCode());
5115             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5116             
5117         }
5118     },
5119     adjustWidth : function(tag, w){
5120         tag = tag.toLowerCase();
5121         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5122             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5123                 if(tag == 'input'){
5124                     return w + 2;
5125                 }
5126                 if(tag == 'textarea'){
5127                     return w-2;
5128                 }
5129             }else if(Roo.isOpera){
5130                 if(tag == 'input'){
5131                     return w + 2;
5132                 }
5133                 if(tag == 'textarea'){
5134                     return w-2;
5135                 }
5136             }
5137         }
5138         return w;
5139     }
5140     
5141 });
5142
5143  
5144 /*
5145  * - LGPL
5146  *
5147  * Input
5148  * 
5149  */
5150
5151 /**
5152  * @class Roo.bootstrap.TextArea
5153  * @extends Roo.bootstrap.Input
5154  * Bootstrap TextArea class
5155  * @cfg {Number} cols Specifies the visible width of a text area
5156  * @cfg {Number} rows Specifies the visible number of lines in a text area
5157  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5158  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5159  * @cfg {string} html text
5160  * 
5161  * @constructor
5162  * Create a new TextArea
5163  * @param {Object} config The config object
5164  */
5165
5166 Roo.bootstrap.TextArea = function(config){
5167     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5168    
5169 };
5170
5171 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5172      
5173     cols : false,
5174     rows : 5,
5175     readOnly : false,
5176     warp : 'soft',
5177     resize : false,
5178     value: false,
5179     html: false,
5180     
5181     getAutoCreate : function(){
5182         
5183         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5184         
5185         var id = Roo.id();
5186         
5187         var cfg = {};
5188         
5189         var input =  {
5190             tag: 'textarea',
5191             id : id,
5192             warp : this.warp,
5193             rows : this.rows,
5194             value : this.value || '',
5195             html: this.html || '',
5196             cls : 'form-control',
5197             placeholder : this.placeholder || '' 
5198             
5199         };
5200         
5201         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5202             input.maxLength = this.maxLength;
5203         }
5204         
5205         if(this.resize){
5206             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5207         }
5208         
5209         if(this.cols){
5210             input.cols = this.cols;
5211         }
5212         
5213         if (this.readOnly) {
5214             input.readonly = true;
5215         }
5216         
5217         if (this.name) {
5218             input.name = this.name;
5219         }
5220         
5221         if (this.size) {
5222             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5223         }
5224         
5225         var settings=this;
5226         ['xs','sm','md','lg'].map(function(size){
5227             if (settings[size]) {
5228                 cfg.cls += ' col-' + size + '-' + settings[size];
5229             }
5230         });
5231         
5232         var inputblock = input;
5233         
5234         if (this.before || this.after) {
5235             
5236             inputblock = {
5237                 cls : 'input-group',
5238                 cn :  [] 
5239             };
5240             if (this.before) {
5241                 inputblock.cn.push({
5242                     tag :'span',
5243                     cls : 'input-group-addon',
5244                     html : this.before
5245                 });
5246             }
5247             inputblock.cn.push(input);
5248             if (this.after) {
5249                 inputblock.cn.push({
5250                     tag :'span',
5251                     cls : 'input-group-addon',
5252                     html : this.after
5253                 });
5254             }
5255             
5256         }
5257         
5258         if (align ==='left' && this.fieldLabel.length) {
5259                 Roo.log("left and has label");
5260                 cfg.cn = [
5261                     
5262                     {
5263                         tag: 'label',
5264                         'for' :  id,
5265                         cls : 'control-label col-sm-' + this.labelWidth,
5266                         html : this.fieldLabel
5267                         
5268                     },
5269                     {
5270                         cls : "col-sm-" + (12 - this.labelWidth), 
5271                         cn: [
5272                             inputblock
5273                         ]
5274                     }
5275                     
5276                 ];
5277         } else if ( this.fieldLabel.length) {
5278                 Roo.log(" label");
5279                  cfg.cn = [
5280                    
5281                     {
5282                         tag: 'label',
5283                         //cls : 'input-group-addon',
5284                         html : this.fieldLabel
5285                         
5286                     },
5287                     
5288                     inputblock
5289                     
5290                 ];
5291
5292         } else {
5293             
5294                    Roo.log(" no label && no align");
5295                 cfg.cn = [
5296                     
5297                         inputblock
5298                     
5299                 ];
5300                 
5301                 
5302         }
5303         
5304         if (this.disabled) {
5305             input.disabled=true;
5306         }
5307         
5308         return cfg;
5309         
5310     },
5311     /**
5312      * return the real textarea element.
5313      */
5314     inputEl: function ()
5315     {
5316         return this.el.select('textarea.form-control',true).first();
5317     }
5318 });
5319
5320  
5321 /*
5322  * - LGPL
5323  *
5324  * trigger field - base class for combo..
5325  * 
5326  */
5327  
5328 /**
5329  * @class Roo.bootstrap.TriggerField
5330  * @extends Roo.bootstrap.Input
5331  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5332  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5333  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5334  * for which you can provide a custom implementation.  For example:
5335  * <pre><code>
5336 var trigger = new Roo.bootstrap.TriggerField();
5337 trigger.onTriggerClick = myTriggerFn;
5338 trigger.applyTo('my-field');
5339 </code></pre>
5340  *
5341  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5342  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5343  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5344  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5345  * @constructor
5346  * Create a new TriggerField.
5347  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5348  * to the base TextField)
5349  */
5350 Roo.bootstrap.TriggerField = function(config){
5351     this.mimicing = false;
5352     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5353 };
5354
5355 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5356     /**
5357      * @cfg {String} triggerClass A CSS class to apply to the trigger
5358      */
5359      /**
5360      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5361      */
5362     hideTrigger:false,
5363
5364     /** @cfg {Boolean} grow @hide */
5365     /** @cfg {Number} growMin @hide */
5366     /** @cfg {Number} growMax @hide */
5367
5368     /**
5369      * @hide 
5370      * @method
5371      */
5372     autoSize: Roo.emptyFn,
5373     // private
5374     monitorTab : true,
5375     // private
5376     deferHeight : true,
5377
5378     
5379     actionMode : 'wrap',
5380     
5381     
5382     
5383     getAutoCreate : function(){
5384        
5385         var parent = this.parent();
5386         
5387         var align = this.parentLabelAlign();
5388         
5389         var id = Roo.id();
5390         
5391         var cfg = {
5392             cls: 'form-group' //input-group
5393         };
5394         
5395         
5396         var input =  {
5397             tag: 'input',
5398             id : id,
5399             type : this.inputType,
5400             cls : 'form-control',
5401             autocomplete: 'off',
5402             placeholder : this.placeholder || '' 
5403             
5404         };
5405         if (this.name) {
5406             input.name = this.name;
5407         }
5408         if (this.size) {
5409             input.cls += ' input-' + this.size;
5410         }
5411         
5412         if (this.disabled) {
5413             input.disabled=true;
5414         }
5415         
5416         var inputblock = input;
5417         
5418         if (this.before || this.after) {
5419             
5420             inputblock = {
5421                 cls : 'input-group',
5422                 cn :  [] 
5423             };
5424             if (this.before) {
5425                 inputblock.cn.push({
5426                     tag :'span',
5427                     cls : 'input-group-addon',
5428                     html : this.before
5429                 });
5430             }
5431             inputblock.cn.push(input);
5432             if (this.after) {
5433                 inputblock.cn.push({
5434                     tag :'span',
5435                     cls : 'input-group-addon',
5436                     html : this.after
5437                 });
5438             }
5439             
5440         };
5441         
5442         var box = {
5443             tag: 'div',
5444             cn: [
5445                 {
5446                     tag: 'input',
5447                     type : 'hidden',
5448                     cls: 'form-hidden-field'
5449                 },
5450                 inputblock
5451             ]
5452             
5453         };
5454         
5455         if(this.multiple){
5456             Roo.log('multiple');
5457             
5458             box = {
5459                 tag: 'div',
5460                 cn: [
5461                     {
5462                         tag: 'input',
5463                         type : 'hidden',
5464                         cls: 'form-hidden-field'
5465                     },
5466                     {
5467                         tag: 'ul',
5468                         cls: 'select2-choices',
5469                         cn:[
5470                             {
5471                                 tag: 'li',
5472                                 cls: 'select2-search-field',
5473                                 cn: [
5474
5475                                     inputblock
5476                                 ]
5477                             }
5478                         ]
5479                     }
5480                 ]
5481             }
5482         };
5483         
5484         var combobox = {
5485             cls: 'select2-container input-group',
5486             cn: [
5487                 box,
5488                 {
5489                     tag: 'ul',
5490                     cls: 'typeahead typeahead-long dropdown-menu',
5491                     style: 'display:none'
5492                 }
5493             ]
5494         };
5495         
5496         if(!this.multiple){
5497             combobox.cn.push({
5498                 tag :'span',
5499                 cls : 'input-group-addon btn dropdown-toggle',
5500                 cn : [
5501                     {
5502                         tag: 'span',
5503                         cls: 'caret'
5504                     },
5505                     {
5506                         tag: 'span',
5507                         cls: 'combobox-clear',
5508                         cn  : [
5509                             {
5510                                 tag : 'i',
5511                                 cls: 'icon-remove'
5512                             }
5513                         ]
5514                     }
5515                 ]
5516
5517             })
5518         }
5519         
5520         if(this.multiple){
5521             combobox.cls += ' select2-container-multi';
5522         }
5523         
5524         if (align ==='left' && this.fieldLabel.length) {
5525             
5526                 Roo.log("left and has label");
5527                 cfg.cn = [
5528                     
5529                     {
5530                         tag: 'label',
5531                         'for' :  id,
5532                         cls : 'control-label col-sm-' + this.labelWidth,
5533                         html : this.fieldLabel
5534                         
5535                     },
5536                     {
5537                         cls : "col-sm-" + (12 - this.labelWidth), 
5538                         cn: [
5539                             combobox
5540                         ]
5541                     }
5542                     
5543                 ];
5544         } else if ( this.fieldLabel.length) {
5545                 Roo.log(" label");
5546                  cfg.cn = [
5547                    
5548                     {
5549                         tag: 'label',
5550                         //cls : 'input-group-addon',
5551                         html : this.fieldLabel
5552                         
5553                     },
5554                     
5555                     combobox
5556                     
5557                 ];
5558
5559         } else {
5560             
5561                 Roo.log(" no label && no align");
5562                 cfg = combobox
5563                      
5564                 
5565         }
5566          
5567         var settings=this;
5568         ['xs','sm','md','lg'].map(function(size){
5569             if (settings[size]) {
5570                 cfg.cls += ' col-' + size + '-' + settings[size];
5571             }
5572         });
5573         
5574         return cfg;
5575         
5576     },
5577     
5578     
5579     
5580     // private
5581     onResize : function(w, h){
5582 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5583 //        if(typeof w == 'number'){
5584 //            var x = w - this.trigger.getWidth();
5585 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5586 //            this.trigger.setStyle('left', x+'px');
5587 //        }
5588     },
5589
5590     // private
5591     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5592
5593     // private
5594     getResizeEl : function(){
5595         return this.inputEl();
5596     },
5597
5598     // private
5599     getPositionEl : function(){
5600         return this.inputEl();
5601     },
5602
5603     // private
5604     alignErrorIcon : function(){
5605         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5606     },
5607
5608     // private
5609     initEvents : function(){
5610         
5611         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5612         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5613         if(!this.multiple){
5614             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5615             if(this.hideTrigger){
5616                 this.trigger.setDisplayed(false);
5617             }
5618             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5619         }
5620         
5621         if(this.multiple){
5622             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5623         }
5624         
5625         //this.trigger.addClassOnOver('x-form-trigger-over');
5626         //this.trigger.addClassOnClick('x-form-trigger-click');
5627         
5628         //if(!this.width){
5629         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5630         //}
5631     },
5632
5633     // private
5634     initTrigger : function(){
5635        
5636     },
5637
5638     // private
5639     onDestroy : function(){
5640         if(this.trigger){
5641             this.trigger.removeAllListeners();
5642           //  this.trigger.remove();
5643         }
5644         //if(this.wrap){
5645         //    this.wrap.remove();
5646         //}
5647         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5648     },
5649
5650     // private
5651     onFocus : function(){
5652         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5653         /*
5654         if(!this.mimicing){
5655             this.wrap.addClass('x-trigger-wrap-focus');
5656             this.mimicing = true;
5657             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5658             if(this.monitorTab){
5659                 this.el.on("keydown", this.checkTab, this);
5660             }
5661         }
5662         */
5663     },
5664
5665     // private
5666     checkTab : function(e){
5667         if(e.getKey() == e.TAB){
5668             this.triggerBlur();
5669         }
5670     },
5671
5672     // private
5673     onBlur : function(){
5674         // do nothing
5675     },
5676
5677     // private
5678     mimicBlur : function(e, t){
5679         /*
5680         if(!this.wrap.contains(t) && this.validateBlur()){
5681             this.triggerBlur();
5682         }
5683         */
5684     },
5685
5686     // private
5687     triggerBlur : function(){
5688         this.mimicing = false;
5689         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5690         if(this.monitorTab){
5691             this.el.un("keydown", this.checkTab, this);
5692         }
5693         //this.wrap.removeClass('x-trigger-wrap-focus');
5694         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5695     },
5696
5697     // private
5698     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5699     validateBlur : function(e, t){
5700         return true;
5701     },
5702
5703     // private
5704     onDisable : function(){
5705         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5706         //if(this.wrap){
5707         //    this.wrap.addClass('x-item-disabled');
5708         //}
5709     },
5710
5711     // private
5712     onEnable : function(){
5713         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5714         //if(this.wrap){
5715         //    this.el.removeClass('x-item-disabled');
5716         //}
5717     },
5718
5719     // private
5720     onShow : function(){
5721         var ae = this.getActionEl();
5722         
5723         if(ae){
5724             ae.dom.style.display = '';
5725             ae.dom.style.visibility = 'visible';
5726         }
5727     },
5728
5729     // private
5730     
5731     onHide : function(){
5732         var ae = this.getActionEl();
5733         ae.dom.style.display = 'none';
5734     },
5735
5736     /**
5737      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5738      * by an implementing function.
5739      * @method
5740      * @param {EventObject} e
5741      */
5742     onTriggerClick : Roo.emptyFn
5743 });
5744  /*
5745  * Based on:
5746  * Ext JS Library 1.1.1
5747  * Copyright(c) 2006-2007, Ext JS, LLC.
5748  *
5749  * Originally Released Under LGPL - original licence link has changed is not relivant.
5750  *
5751  * Fork - LGPL
5752  * <script type="text/javascript">
5753  */
5754
5755
5756 /**
5757  * @class Roo.data.SortTypes
5758  * @singleton
5759  * Defines the default sorting (casting?) comparison functions used when sorting data.
5760  */
5761 Roo.data.SortTypes = {
5762     /**
5763      * Default sort that does nothing
5764      * @param {Mixed} s The value being converted
5765      * @return {Mixed} The comparison value
5766      */
5767     none : function(s){
5768         return s;
5769     },
5770     
5771     /**
5772      * The regular expression used to strip tags
5773      * @type {RegExp}
5774      * @property
5775      */
5776     stripTagsRE : /<\/?[^>]+>/gi,
5777     
5778     /**
5779      * Strips all HTML tags to sort on text only
5780      * @param {Mixed} s The value being converted
5781      * @return {String} The comparison value
5782      */
5783     asText : function(s){
5784         return String(s).replace(this.stripTagsRE, "");
5785     },
5786     
5787     /**
5788      * Strips all HTML tags to sort on text only - Case insensitive
5789      * @param {Mixed} s The value being converted
5790      * @return {String} The comparison value
5791      */
5792     asUCText : function(s){
5793         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5794     },
5795     
5796     /**
5797      * Case insensitive string
5798      * @param {Mixed} s The value being converted
5799      * @return {String} The comparison value
5800      */
5801     asUCString : function(s) {
5802         return String(s).toUpperCase();
5803     },
5804     
5805     /**
5806      * Date sorting
5807      * @param {Mixed} s The value being converted
5808      * @return {Number} The comparison value
5809      */
5810     asDate : function(s) {
5811         if(!s){
5812             return 0;
5813         }
5814         if(s instanceof Date){
5815             return s.getTime();
5816         }
5817         return Date.parse(String(s));
5818     },
5819     
5820     /**
5821      * Float sorting
5822      * @param {Mixed} s The value being converted
5823      * @return {Float} The comparison value
5824      */
5825     asFloat : function(s) {
5826         var val = parseFloat(String(s).replace(/,/g, ""));
5827         if(isNaN(val)) val = 0;
5828         return val;
5829     },
5830     
5831     /**
5832      * Integer sorting
5833      * @param {Mixed} s The value being converted
5834      * @return {Number} The comparison value
5835      */
5836     asInt : function(s) {
5837         var val = parseInt(String(s).replace(/,/g, ""));
5838         if(isNaN(val)) val = 0;
5839         return val;
5840     }
5841 };/*
5842  * Based on:
5843  * Ext JS Library 1.1.1
5844  * Copyright(c) 2006-2007, Ext JS, LLC.
5845  *
5846  * Originally Released Under LGPL - original licence link has changed is not relivant.
5847  *
5848  * Fork - LGPL
5849  * <script type="text/javascript">
5850  */
5851
5852 /**
5853 * @class Roo.data.Record
5854  * Instances of this class encapsulate both record <em>definition</em> information, and record
5855  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5856  * to access Records cached in an {@link Roo.data.Store} object.<br>
5857  * <p>
5858  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5859  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5860  * objects.<br>
5861  * <p>
5862  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5863  * @constructor
5864  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5865  * {@link #create}. The parameters are the same.
5866  * @param {Array} data An associative Array of data values keyed by the field name.
5867  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5868  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5869  * not specified an integer id is generated.
5870  */
5871 Roo.data.Record = function(data, id){
5872     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5873     this.data = data;
5874 };
5875
5876 /**
5877  * Generate a constructor for a specific record layout.
5878  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5879  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5880  * Each field definition object may contain the following properties: <ul>
5881  * <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,
5882  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5883  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5884  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5885  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5886  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5887  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5888  * this may be omitted.</p></li>
5889  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5890  * <ul><li>auto (Default, implies no conversion)</li>
5891  * <li>string</li>
5892  * <li>int</li>
5893  * <li>float</li>
5894  * <li>boolean</li>
5895  * <li>date</li></ul></p></li>
5896  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5897  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5898  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5899  * by the Reader into an object that will be stored in the Record. It is passed the
5900  * following parameters:<ul>
5901  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5902  * </ul></p></li>
5903  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5904  * </ul>
5905  * <br>usage:<br><pre><code>
5906 var TopicRecord = Roo.data.Record.create(
5907     {name: 'title', mapping: 'topic_title'},
5908     {name: 'author', mapping: 'username'},
5909     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5910     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5911     {name: 'lastPoster', mapping: 'user2'},
5912     {name: 'excerpt', mapping: 'post_text'}
5913 );
5914
5915 var myNewRecord = new TopicRecord({
5916     title: 'Do my job please',
5917     author: 'noobie',
5918     totalPosts: 1,
5919     lastPost: new Date(),
5920     lastPoster: 'Animal',
5921     excerpt: 'No way dude!'
5922 });
5923 myStore.add(myNewRecord);
5924 </code></pre>
5925  * @method create
5926  * @static
5927  */
5928 Roo.data.Record.create = function(o){
5929     var f = function(){
5930         f.superclass.constructor.apply(this, arguments);
5931     };
5932     Roo.extend(f, Roo.data.Record);
5933     var p = f.prototype;
5934     p.fields = new Roo.util.MixedCollection(false, function(field){
5935         return field.name;
5936     });
5937     for(var i = 0, len = o.length; i < len; i++){
5938         p.fields.add(new Roo.data.Field(o[i]));
5939     }
5940     f.getField = function(name){
5941         return p.fields.get(name);  
5942     };
5943     return f;
5944 };
5945
5946 Roo.data.Record.AUTO_ID = 1000;
5947 Roo.data.Record.EDIT = 'edit';
5948 Roo.data.Record.REJECT = 'reject';
5949 Roo.data.Record.COMMIT = 'commit';
5950
5951 Roo.data.Record.prototype = {
5952     /**
5953      * Readonly flag - true if this record has been modified.
5954      * @type Boolean
5955      */
5956     dirty : false,
5957     editing : false,
5958     error: null,
5959     modified: null,
5960
5961     // private
5962     join : function(store){
5963         this.store = store;
5964     },
5965
5966     /**
5967      * Set the named field to the specified value.
5968      * @param {String} name The name of the field to set.
5969      * @param {Object} value The value to set the field to.
5970      */
5971     set : function(name, value){
5972         if(this.data[name] == value){
5973             return;
5974         }
5975         this.dirty = true;
5976         if(!this.modified){
5977             this.modified = {};
5978         }
5979         if(typeof this.modified[name] == 'undefined'){
5980             this.modified[name] = this.data[name];
5981         }
5982         this.data[name] = value;
5983         if(!this.editing && this.store){
5984             this.store.afterEdit(this);
5985         }       
5986     },
5987
5988     /**
5989      * Get the value of the named field.
5990      * @param {String} name The name of the field to get the value of.
5991      * @return {Object} The value of the field.
5992      */
5993     get : function(name){
5994         return this.data[name]; 
5995     },
5996
5997     // private
5998     beginEdit : function(){
5999         this.editing = true;
6000         this.modified = {}; 
6001     },
6002
6003     // private
6004     cancelEdit : function(){
6005         this.editing = false;
6006         delete this.modified;
6007     },
6008
6009     // private
6010     endEdit : function(){
6011         this.editing = false;
6012         if(this.dirty && this.store){
6013             this.store.afterEdit(this);
6014         }
6015     },
6016
6017     /**
6018      * Usually called by the {@link Roo.data.Store} which owns the Record.
6019      * Rejects all changes made to the Record since either creation, or the last commit operation.
6020      * Modified fields are reverted to their original values.
6021      * <p>
6022      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6023      * of reject operations.
6024      */
6025     reject : function(){
6026         var m = this.modified;
6027         for(var n in m){
6028             if(typeof m[n] != "function"){
6029                 this.data[n] = m[n];
6030             }
6031         }
6032         this.dirty = false;
6033         delete this.modified;
6034         this.editing = false;
6035         if(this.store){
6036             this.store.afterReject(this);
6037         }
6038     },
6039
6040     /**
6041      * Usually called by the {@link Roo.data.Store} which owns the Record.
6042      * Commits all changes made to the Record since either creation, or the last commit operation.
6043      * <p>
6044      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6045      * of commit operations.
6046      */
6047     commit : function(){
6048         this.dirty = false;
6049         delete this.modified;
6050         this.editing = false;
6051         if(this.store){
6052             this.store.afterCommit(this);
6053         }
6054     },
6055
6056     // private
6057     hasError : function(){
6058         return this.error != null;
6059     },
6060
6061     // private
6062     clearError : function(){
6063         this.error = null;
6064     },
6065
6066     /**
6067      * Creates a copy of this record.
6068      * @param {String} id (optional) A new record id if you don't want to use this record's id
6069      * @return {Record}
6070      */
6071     copy : function(newId) {
6072         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6073     }
6074 };/*
6075  * Based on:
6076  * Ext JS Library 1.1.1
6077  * Copyright(c) 2006-2007, Ext JS, LLC.
6078  *
6079  * Originally Released Under LGPL - original licence link has changed is not relivant.
6080  *
6081  * Fork - LGPL
6082  * <script type="text/javascript">
6083  */
6084
6085
6086
6087 /**
6088  * @class Roo.data.Store
6089  * @extends Roo.util.Observable
6090  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6091  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6092  * <p>
6093  * 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
6094  * has no knowledge of the format of the data returned by the Proxy.<br>
6095  * <p>
6096  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6097  * instances from the data object. These records are cached and made available through accessor functions.
6098  * @constructor
6099  * Creates a new Store.
6100  * @param {Object} config A config object containing the objects needed for the Store to access data,
6101  * and read the data into Records.
6102  */
6103 Roo.data.Store = function(config){
6104     this.data = new Roo.util.MixedCollection(false);
6105     this.data.getKey = function(o){
6106         return o.id;
6107     };
6108     this.baseParams = {};
6109     // private
6110     this.paramNames = {
6111         "start" : "start",
6112         "limit" : "limit",
6113         "sort" : "sort",
6114         "dir" : "dir",
6115         "multisort" : "_multisort"
6116     };
6117
6118     if(config && config.data){
6119         this.inlineData = config.data;
6120         delete config.data;
6121     }
6122
6123     Roo.apply(this, config);
6124     
6125     if(this.reader){ // reader passed
6126         this.reader = Roo.factory(this.reader, Roo.data);
6127         this.reader.xmodule = this.xmodule || false;
6128         if(!this.recordType){
6129             this.recordType = this.reader.recordType;
6130         }
6131         if(this.reader.onMetaChange){
6132             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6133         }
6134     }
6135
6136     if(this.recordType){
6137         this.fields = this.recordType.prototype.fields;
6138     }
6139     this.modified = [];
6140
6141     this.addEvents({
6142         /**
6143          * @event datachanged
6144          * Fires when the data cache has changed, and a widget which is using this Store
6145          * as a Record cache should refresh its view.
6146          * @param {Store} this
6147          */
6148         datachanged : true,
6149         /**
6150          * @event metachange
6151          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6152          * @param {Store} this
6153          * @param {Object} meta The JSON metadata
6154          */
6155         metachange : true,
6156         /**
6157          * @event add
6158          * Fires when Records have been added to the Store
6159          * @param {Store} this
6160          * @param {Roo.data.Record[]} records The array of Records added
6161          * @param {Number} index The index at which the record(s) were added
6162          */
6163         add : true,
6164         /**
6165          * @event remove
6166          * Fires when a Record has been removed from the Store
6167          * @param {Store} this
6168          * @param {Roo.data.Record} record The Record that was removed
6169          * @param {Number} index The index at which the record was removed
6170          */
6171         remove : true,
6172         /**
6173          * @event update
6174          * Fires when a Record has been updated
6175          * @param {Store} this
6176          * @param {Roo.data.Record} record The Record that was updated
6177          * @param {String} operation The update operation being performed.  Value may be one of:
6178          * <pre><code>
6179  Roo.data.Record.EDIT
6180  Roo.data.Record.REJECT
6181  Roo.data.Record.COMMIT
6182          * </code></pre>
6183          */
6184         update : true,
6185         /**
6186          * @event clear
6187          * Fires when the data cache has been cleared.
6188          * @param {Store} this
6189          */
6190         clear : true,
6191         /**
6192          * @event beforeload
6193          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6194          * the load action will be canceled.
6195          * @param {Store} this
6196          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6197          */
6198         beforeload : true,
6199         /**
6200          * @event beforeloadadd
6201          * Fires after a new set of Records has been loaded.
6202          * @param {Store} this
6203          * @param {Roo.data.Record[]} records The Records that were loaded
6204          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6205          */
6206         beforeloadadd : true,
6207         /**
6208          * @event load
6209          * Fires after a new set of Records has been loaded, before they are added to the store.
6210          * @param {Store} this
6211          * @param {Roo.data.Record[]} records The Records that were loaded
6212          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6213          * @params {Object} return from reader
6214          */
6215         load : true,
6216         /**
6217          * @event loadexception
6218          * Fires if an exception occurs in the Proxy during loading.
6219          * Called with the signature of the Proxy's "loadexception" event.
6220          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6221          * 
6222          * @param {Proxy} 
6223          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6224          * @param {Object} load options 
6225          * @param {Object} jsonData from your request (normally this contains the Exception)
6226          */
6227         loadexception : true
6228     });
6229     
6230     if(this.proxy){
6231         this.proxy = Roo.factory(this.proxy, Roo.data);
6232         this.proxy.xmodule = this.xmodule || false;
6233         this.relayEvents(this.proxy,  ["loadexception"]);
6234     }
6235     this.sortToggle = {};
6236     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6237
6238     Roo.data.Store.superclass.constructor.call(this);
6239
6240     if(this.inlineData){
6241         this.loadData(this.inlineData);
6242         delete this.inlineData;
6243     }
6244 };
6245
6246 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6247      /**
6248     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6249     * without a remote query - used by combo/forms at present.
6250     */
6251     
6252     /**
6253     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6254     */
6255     /**
6256     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6257     */
6258     /**
6259     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6260     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6261     */
6262     /**
6263     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6264     * on any HTTP request
6265     */
6266     /**
6267     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6268     */
6269     /**
6270     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6271     */
6272     multiSort: false,
6273     /**
6274     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6275     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6276     */
6277     remoteSort : false,
6278
6279     /**
6280     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6281      * loaded or when a record is removed. (defaults to false).
6282     */
6283     pruneModifiedRecords : false,
6284
6285     // private
6286     lastOptions : null,
6287
6288     /**
6289      * Add Records to the Store and fires the add event.
6290      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6291      */
6292     add : function(records){
6293         records = [].concat(records);
6294         for(var i = 0, len = records.length; i < len; i++){
6295             records[i].join(this);
6296         }
6297         var index = this.data.length;
6298         this.data.addAll(records);
6299         this.fireEvent("add", this, records, index);
6300     },
6301
6302     /**
6303      * Remove a Record from the Store and fires the remove event.
6304      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6305      */
6306     remove : function(record){
6307         var index = this.data.indexOf(record);
6308         this.data.removeAt(index);
6309         if(this.pruneModifiedRecords){
6310             this.modified.remove(record);
6311         }
6312         this.fireEvent("remove", this, record, index);
6313     },
6314
6315     /**
6316      * Remove all Records from the Store and fires the clear event.
6317      */
6318     removeAll : function(){
6319         this.data.clear();
6320         if(this.pruneModifiedRecords){
6321             this.modified = [];
6322         }
6323         this.fireEvent("clear", this);
6324     },
6325
6326     /**
6327      * Inserts Records to the Store at the given index and fires the add event.
6328      * @param {Number} index The start index at which to insert the passed Records.
6329      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6330      */
6331     insert : function(index, records){
6332         records = [].concat(records);
6333         for(var i = 0, len = records.length; i < len; i++){
6334             this.data.insert(index, records[i]);
6335             records[i].join(this);
6336         }
6337         this.fireEvent("add", this, records, index);
6338     },
6339
6340     /**
6341      * Get the index within the cache of the passed Record.
6342      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6343      * @return {Number} The index of the passed Record. Returns -1 if not found.
6344      */
6345     indexOf : function(record){
6346         return this.data.indexOf(record);
6347     },
6348
6349     /**
6350      * Get the index within the cache of the Record with the passed id.
6351      * @param {String} id The id of the Record to find.
6352      * @return {Number} The index of the Record. Returns -1 if not found.
6353      */
6354     indexOfId : function(id){
6355         return this.data.indexOfKey(id);
6356     },
6357
6358     /**
6359      * Get the Record with the specified id.
6360      * @param {String} id The id of the Record to find.
6361      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6362      */
6363     getById : function(id){
6364         return this.data.key(id);
6365     },
6366
6367     /**
6368      * Get the Record at the specified index.
6369      * @param {Number} index The index of the Record to find.
6370      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6371      */
6372     getAt : function(index){
6373         return this.data.itemAt(index);
6374     },
6375
6376     /**
6377      * Returns a range of Records between specified indices.
6378      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6379      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6380      * @return {Roo.data.Record[]} An array of Records
6381      */
6382     getRange : function(start, end){
6383         return this.data.getRange(start, end);
6384     },
6385
6386     // private
6387     storeOptions : function(o){
6388         o = Roo.apply({}, o);
6389         delete o.callback;
6390         delete o.scope;
6391         this.lastOptions = o;
6392     },
6393
6394     /**
6395      * Loads the Record cache from the configured Proxy using the configured Reader.
6396      * <p>
6397      * If using remote paging, then the first load call must specify the <em>start</em>
6398      * and <em>limit</em> properties in the options.params property to establish the initial
6399      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6400      * <p>
6401      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6402      * and this call will return before the new data has been loaded. Perform any post-processing
6403      * in a callback function, or in a "load" event handler.</strong>
6404      * <p>
6405      * @param {Object} options An object containing properties which control loading options:<ul>
6406      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6407      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6408      * passed the following arguments:<ul>
6409      * <li>r : Roo.data.Record[]</li>
6410      * <li>options: Options object from the load call</li>
6411      * <li>success: Boolean success indicator</li></ul></li>
6412      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6413      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6414      * </ul>
6415      */
6416     load : function(options){
6417         options = options || {};
6418         if(this.fireEvent("beforeload", this, options) !== false){
6419             this.storeOptions(options);
6420             var p = Roo.apply(options.params || {}, this.baseParams);
6421             // if meta was not loaded from remote source.. try requesting it.
6422             if (!this.reader.metaFromRemote) {
6423                 p._requestMeta = 1;
6424             }
6425             if(this.sortInfo && this.remoteSort){
6426                 var pn = this.paramNames;
6427                 p[pn["sort"]] = this.sortInfo.field;
6428                 p[pn["dir"]] = this.sortInfo.direction;
6429             }
6430             if (this.multiSort) {
6431                 var pn = this.paramNames;
6432                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6433             }
6434             
6435             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6436         }
6437     },
6438
6439     /**
6440      * Reloads the Record cache from the configured Proxy using the configured Reader and
6441      * the options from the last load operation performed.
6442      * @param {Object} options (optional) An object containing properties which may override the options
6443      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6444      * the most recently used options are reused).
6445      */
6446     reload : function(options){
6447         this.load(Roo.applyIf(options||{}, this.lastOptions));
6448     },
6449
6450     // private
6451     // Called as a callback by the Reader during a load operation.
6452     loadRecords : function(o, options, success){
6453         if(!o || success === false){
6454             if(success !== false){
6455                 this.fireEvent("load", this, [], options, o);
6456             }
6457             if(options.callback){
6458                 options.callback.call(options.scope || this, [], options, false);
6459             }
6460             return;
6461         }
6462         // if data returned failure - throw an exception.
6463         if (o.success === false) {
6464             // show a message if no listener is registered.
6465             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6466                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6467             }
6468             // loadmask wil be hooked into this..
6469             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6470             return;
6471         }
6472         var r = o.records, t = o.totalRecords || r.length;
6473         
6474         this.fireEvent("beforeloadadd", this, r, options, o);
6475         
6476         if(!options || options.add !== true){
6477             if(this.pruneModifiedRecords){
6478                 this.modified = [];
6479             }
6480             for(var i = 0, len = r.length; i < len; i++){
6481                 r[i].join(this);
6482             }
6483             if(this.snapshot){
6484                 this.data = this.snapshot;
6485                 delete this.snapshot;
6486             }
6487             this.data.clear();
6488             this.data.addAll(r);
6489             this.totalLength = t;
6490             this.applySort();
6491             this.fireEvent("datachanged", this);
6492         }else{
6493             this.totalLength = Math.max(t, this.data.length+r.length);
6494             this.add(r);
6495         }
6496         this.fireEvent("load", this, r, options, o);
6497         if(options.callback){
6498             options.callback.call(options.scope || this, r, options, true);
6499         }
6500     },
6501
6502
6503     /**
6504      * Loads data from a passed data block. A Reader which understands the format of the data
6505      * must have been configured in the constructor.
6506      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6507      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6508      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6509      */
6510     loadData : function(o, append){
6511         var r = this.reader.readRecords(o);
6512         this.loadRecords(r, {add: append}, true);
6513     },
6514
6515     /**
6516      * Gets the number of cached records.
6517      * <p>
6518      * <em>If using paging, this may not be the total size of the dataset. If the data object
6519      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6520      * the data set size</em>
6521      */
6522     getCount : function(){
6523         return this.data.length || 0;
6524     },
6525
6526     /**
6527      * Gets the total number of records in the dataset as returned by the server.
6528      * <p>
6529      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6530      * the dataset size</em>
6531      */
6532     getTotalCount : function(){
6533         return this.totalLength || 0;
6534     },
6535
6536     /**
6537      * Returns the sort state of the Store as an object with two properties:
6538      * <pre><code>
6539  field {String} The name of the field by which the Records are sorted
6540  direction {String} The sort order, "ASC" or "DESC"
6541      * </code></pre>
6542      */
6543     getSortState : function(){
6544         return this.sortInfo;
6545     },
6546
6547     // private
6548     applySort : function(){
6549         if(this.sortInfo && !this.remoteSort){
6550             var s = this.sortInfo, f = s.field;
6551             var st = this.fields.get(f).sortType;
6552             var fn = function(r1, r2){
6553                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6554                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6555             };
6556             this.data.sort(s.direction, fn);
6557             if(this.snapshot && this.snapshot != this.data){
6558                 this.snapshot.sort(s.direction, fn);
6559             }
6560         }
6561     },
6562
6563     /**
6564      * Sets the default sort column and order to be used by the next load operation.
6565      * @param {String} fieldName The name of the field to sort by.
6566      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6567      */
6568     setDefaultSort : function(field, dir){
6569         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6570     },
6571
6572     /**
6573      * Sort the Records.
6574      * If remote sorting is used, the sort is performed on the server, and the cache is
6575      * reloaded. If local sorting is used, the cache is sorted internally.
6576      * @param {String} fieldName The name of the field to sort by.
6577      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6578      */
6579     sort : function(fieldName, dir){
6580         var f = this.fields.get(fieldName);
6581         if(!dir){
6582             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6583             
6584             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6585                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6586             }else{
6587                 dir = f.sortDir;
6588             }
6589         }
6590         this.sortToggle[f.name] = dir;
6591         this.sortInfo = {field: f.name, direction: dir};
6592         if(!this.remoteSort){
6593             this.applySort();
6594             this.fireEvent("datachanged", this);
6595         }else{
6596             this.load(this.lastOptions);
6597         }
6598     },
6599
6600     /**
6601      * Calls the specified function for each of the Records in the cache.
6602      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6603      * Returning <em>false</em> aborts and exits the iteration.
6604      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6605      */
6606     each : function(fn, scope){
6607         this.data.each(fn, scope);
6608     },
6609
6610     /**
6611      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6612      * (e.g., during paging).
6613      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6614      */
6615     getModifiedRecords : function(){
6616         return this.modified;
6617     },
6618
6619     // private
6620     createFilterFn : function(property, value, anyMatch){
6621         if(!value.exec){ // not a regex
6622             value = String(value);
6623             if(value.length == 0){
6624                 return false;
6625             }
6626             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6627         }
6628         return function(r){
6629             return value.test(r.data[property]);
6630         };
6631     },
6632
6633     /**
6634      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6635      * @param {String} property A field on your records
6636      * @param {Number} start The record index to start at (defaults to 0)
6637      * @param {Number} end The last record index to include (defaults to length - 1)
6638      * @return {Number} The sum
6639      */
6640     sum : function(property, start, end){
6641         var rs = this.data.items, v = 0;
6642         start = start || 0;
6643         end = (end || end === 0) ? end : rs.length-1;
6644
6645         for(var i = start; i <= end; i++){
6646             v += (rs[i].data[property] || 0);
6647         }
6648         return v;
6649     },
6650
6651     /**
6652      * Filter the records by a specified property.
6653      * @param {String} field A field on your records
6654      * @param {String/RegExp} value Either a string that the field
6655      * should start with or a RegExp to test against the field
6656      * @param {Boolean} anyMatch True to match any part not just the beginning
6657      */
6658     filter : function(property, value, anyMatch){
6659         var fn = this.createFilterFn(property, value, anyMatch);
6660         return fn ? this.filterBy(fn) : this.clearFilter();
6661     },
6662
6663     /**
6664      * Filter by a function. The specified function will be called with each
6665      * record in this data source. If the function returns true the record is included,
6666      * otherwise it is filtered.
6667      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6668      * @param {Object} scope (optional) The scope of the function (defaults to this)
6669      */
6670     filterBy : function(fn, scope){
6671         this.snapshot = this.snapshot || this.data;
6672         this.data = this.queryBy(fn, scope||this);
6673         this.fireEvent("datachanged", this);
6674     },
6675
6676     /**
6677      * Query the records by a specified property.
6678      * @param {String} field A field on your records
6679      * @param {String/RegExp} value Either a string that the field
6680      * should start with or a RegExp to test against the field
6681      * @param {Boolean} anyMatch True to match any part not just the beginning
6682      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6683      */
6684     query : function(property, value, anyMatch){
6685         var fn = this.createFilterFn(property, value, anyMatch);
6686         return fn ? this.queryBy(fn) : this.data.clone();
6687     },
6688
6689     /**
6690      * Query by a function. The specified function will be called with each
6691      * record in this data source. If the function returns true the record is included
6692      * in the results.
6693      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6694      * @param {Object} scope (optional) The scope of the function (defaults to this)
6695       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6696      **/
6697     queryBy : function(fn, scope){
6698         var data = this.snapshot || this.data;
6699         return data.filterBy(fn, scope||this);
6700     },
6701
6702     /**
6703      * Collects unique values for a particular dataIndex from this store.
6704      * @param {String} dataIndex The property to collect
6705      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6706      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6707      * @return {Array} An array of the unique values
6708      **/
6709     collect : function(dataIndex, allowNull, bypassFilter){
6710         var d = (bypassFilter === true && this.snapshot) ?
6711                 this.snapshot.items : this.data.items;
6712         var v, sv, r = [], l = {};
6713         for(var i = 0, len = d.length; i < len; i++){
6714             v = d[i].data[dataIndex];
6715             sv = String(v);
6716             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6717                 l[sv] = true;
6718                 r[r.length] = v;
6719             }
6720         }
6721         return r;
6722     },
6723
6724     /**
6725      * Revert to a view of the Record cache with no filtering applied.
6726      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6727      */
6728     clearFilter : function(suppressEvent){
6729         if(this.snapshot && this.snapshot != this.data){
6730             this.data = this.snapshot;
6731             delete this.snapshot;
6732             if(suppressEvent !== true){
6733                 this.fireEvent("datachanged", this);
6734             }
6735         }
6736     },
6737
6738     // private
6739     afterEdit : function(record){
6740         if(this.modified.indexOf(record) == -1){
6741             this.modified.push(record);
6742         }
6743         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6744     },
6745     
6746     // private
6747     afterReject : function(record){
6748         this.modified.remove(record);
6749         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6750     },
6751
6752     // private
6753     afterCommit : function(record){
6754         this.modified.remove(record);
6755         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6756     },
6757
6758     /**
6759      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6760      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6761      */
6762     commitChanges : function(){
6763         var m = this.modified.slice(0);
6764         this.modified = [];
6765         for(var i = 0, len = m.length; i < len; i++){
6766             m[i].commit();
6767         }
6768     },
6769
6770     /**
6771      * Cancel outstanding changes on all changed records.
6772      */
6773     rejectChanges : function(){
6774         var m = this.modified.slice(0);
6775         this.modified = [];
6776         for(var i = 0, len = m.length; i < len; i++){
6777             m[i].reject();
6778         }
6779     },
6780
6781     onMetaChange : function(meta, rtype, o){
6782         this.recordType = rtype;
6783         this.fields = rtype.prototype.fields;
6784         delete this.snapshot;
6785         this.sortInfo = meta.sortInfo || this.sortInfo;
6786         this.modified = [];
6787         this.fireEvent('metachange', this, this.reader.meta);
6788     },
6789     
6790     moveIndex : function(data, type)
6791     {
6792         var index = this.indexOf(data);
6793         
6794         var newIndex = index + type;
6795         
6796         this.remove(data);
6797         
6798         this.insert(newIndex, data);
6799         
6800     }
6801 });/*
6802  * Based on:
6803  * Ext JS Library 1.1.1
6804  * Copyright(c) 2006-2007, Ext JS, LLC.
6805  *
6806  * Originally Released Under LGPL - original licence link has changed is not relivant.
6807  *
6808  * Fork - LGPL
6809  * <script type="text/javascript">
6810  */
6811
6812 /**
6813  * @class Roo.data.SimpleStore
6814  * @extends Roo.data.Store
6815  * Small helper class to make creating Stores from Array data easier.
6816  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6817  * @cfg {Array} fields An array of field definition objects, or field name strings.
6818  * @cfg {Array} data The multi-dimensional array of data
6819  * @constructor
6820  * @param {Object} config
6821  */
6822 Roo.data.SimpleStore = function(config){
6823     Roo.data.SimpleStore.superclass.constructor.call(this, {
6824         isLocal : true,
6825         reader: new Roo.data.ArrayReader({
6826                 id: config.id
6827             },
6828             Roo.data.Record.create(config.fields)
6829         ),
6830         proxy : new Roo.data.MemoryProxy(config.data)
6831     });
6832     this.load();
6833 };
6834 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6835  * Based on:
6836  * Ext JS Library 1.1.1
6837  * Copyright(c) 2006-2007, Ext JS, LLC.
6838  *
6839  * Originally Released Under LGPL - original licence link has changed is not relivant.
6840  *
6841  * Fork - LGPL
6842  * <script type="text/javascript">
6843  */
6844
6845 /**
6846 /**
6847  * @extends Roo.data.Store
6848  * @class Roo.data.JsonStore
6849  * Small helper class to make creating Stores for JSON data easier. <br/>
6850 <pre><code>
6851 var store = new Roo.data.JsonStore({
6852     url: 'get-images.php',
6853     root: 'images',
6854     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6855 });
6856 </code></pre>
6857  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6858  * JsonReader and HttpProxy (unless inline data is provided).</b>
6859  * @cfg {Array} fields An array of field definition objects, or field name strings.
6860  * @constructor
6861  * @param {Object} config
6862  */
6863 Roo.data.JsonStore = function(c){
6864     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6865         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6866         reader: new Roo.data.JsonReader(c, c.fields)
6867     }));
6868 };
6869 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6870  * Based on:
6871  * Ext JS Library 1.1.1
6872  * Copyright(c) 2006-2007, Ext JS, LLC.
6873  *
6874  * Originally Released Under LGPL - original licence link has changed is not relivant.
6875  *
6876  * Fork - LGPL
6877  * <script type="text/javascript">
6878  */
6879
6880  
6881 Roo.data.Field = function(config){
6882     if(typeof config == "string"){
6883         config = {name: config};
6884     }
6885     Roo.apply(this, config);
6886     
6887     if(!this.type){
6888         this.type = "auto";
6889     }
6890     
6891     var st = Roo.data.SortTypes;
6892     // named sortTypes are supported, here we look them up
6893     if(typeof this.sortType == "string"){
6894         this.sortType = st[this.sortType];
6895     }
6896     
6897     // set default sortType for strings and dates
6898     if(!this.sortType){
6899         switch(this.type){
6900             case "string":
6901                 this.sortType = st.asUCString;
6902                 break;
6903             case "date":
6904                 this.sortType = st.asDate;
6905                 break;
6906             default:
6907                 this.sortType = st.none;
6908         }
6909     }
6910
6911     // define once
6912     var stripRe = /[\$,%]/g;
6913
6914     // prebuilt conversion function for this field, instead of
6915     // switching every time we're reading a value
6916     if(!this.convert){
6917         var cv, dateFormat = this.dateFormat;
6918         switch(this.type){
6919             case "":
6920             case "auto":
6921             case undefined:
6922                 cv = function(v){ return v; };
6923                 break;
6924             case "string":
6925                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6926                 break;
6927             case "int":
6928                 cv = function(v){
6929                     return v !== undefined && v !== null && v !== '' ?
6930                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6931                     };
6932                 break;
6933             case "float":
6934                 cv = function(v){
6935                     return v !== undefined && v !== null && v !== '' ?
6936                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6937                     };
6938                 break;
6939             case "bool":
6940             case "boolean":
6941                 cv = function(v){ return v === true || v === "true" || v == 1; };
6942                 break;
6943             case "date":
6944                 cv = function(v){
6945                     if(!v){
6946                         return '';
6947                     }
6948                     if(v instanceof Date){
6949                         return v;
6950                     }
6951                     if(dateFormat){
6952                         if(dateFormat == "timestamp"){
6953                             return new Date(v*1000);
6954                         }
6955                         return Date.parseDate(v, dateFormat);
6956                     }
6957                     var parsed = Date.parse(v);
6958                     return parsed ? new Date(parsed) : null;
6959                 };
6960              break;
6961             
6962         }
6963         this.convert = cv;
6964     }
6965 };
6966
6967 Roo.data.Field.prototype = {
6968     dateFormat: null,
6969     defaultValue: "",
6970     mapping: null,
6971     sortType : null,
6972     sortDir : "ASC"
6973 };/*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984 // Base class for reading structured data from a data source.  This class is intended to be
6985 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6986
6987 /**
6988  * @class Roo.data.DataReader
6989  * Base class for reading structured data from a data source.  This class is intended to be
6990  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6991  */
6992
6993 Roo.data.DataReader = function(meta, recordType){
6994     
6995     this.meta = meta;
6996     
6997     this.recordType = recordType instanceof Array ? 
6998         Roo.data.Record.create(recordType) : recordType;
6999 };
7000
7001 Roo.data.DataReader.prototype = {
7002      /**
7003      * Create an empty record
7004      * @param {Object} data (optional) - overlay some values
7005      * @return {Roo.data.Record} record created.
7006      */
7007     newRow :  function(d) {
7008         var da =  {};
7009         this.recordType.prototype.fields.each(function(c) {
7010             switch( c.type) {
7011                 case 'int' : da[c.name] = 0; break;
7012                 case 'date' : da[c.name] = new Date(); break;
7013                 case 'float' : da[c.name] = 0.0; break;
7014                 case 'boolean' : da[c.name] = false; break;
7015                 default : da[c.name] = ""; break;
7016             }
7017             
7018         });
7019         return new this.recordType(Roo.apply(da, d));
7020     }
7021     
7022 };/*
7023  * Based on:
7024  * Ext JS Library 1.1.1
7025  * Copyright(c) 2006-2007, Ext JS, LLC.
7026  *
7027  * Originally Released Under LGPL - original licence link has changed is not relivant.
7028  *
7029  * Fork - LGPL
7030  * <script type="text/javascript">
7031  */
7032
7033 /**
7034  * @class Roo.data.DataProxy
7035  * @extends Roo.data.Observable
7036  * This class is an abstract base class for implementations which provide retrieval of
7037  * unformatted data objects.<br>
7038  * <p>
7039  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7040  * (of the appropriate type which knows how to parse the data object) to provide a block of
7041  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7042  * <p>
7043  * Custom implementations must implement the load method as described in
7044  * {@link Roo.data.HttpProxy#load}.
7045  */
7046 Roo.data.DataProxy = function(){
7047     this.addEvents({
7048         /**
7049          * @event beforeload
7050          * Fires before a network request is made to retrieve a data object.
7051          * @param {Object} This DataProxy object.
7052          * @param {Object} params The params parameter to the load function.
7053          */
7054         beforeload : true,
7055         /**
7056          * @event load
7057          * Fires before the load method's callback is called.
7058          * @param {Object} This DataProxy object.
7059          * @param {Object} o The data object.
7060          * @param {Object} arg The callback argument object passed to the load function.
7061          */
7062         load : true,
7063         /**
7064          * @event loadexception
7065          * Fires if an Exception occurs during data retrieval.
7066          * @param {Object} This DataProxy object.
7067          * @param {Object} o The data object.
7068          * @param {Object} arg The callback argument object passed to the load function.
7069          * @param {Object} e The Exception.
7070          */
7071         loadexception : true
7072     });
7073     Roo.data.DataProxy.superclass.constructor.call(this);
7074 };
7075
7076 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7077
7078     /**
7079      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7080      */
7081 /*
7082  * Based on:
7083  * Ext JS Library 1.1.1
7084  * Copyright(c) 2006-2007, Ext JS, LLC.
7085  *
7086  * Originally Released Under LGPL - original licence link has changed is not relivant.
7087  *
7088  * Fork - LGPL
7089  * <script type="text/javascript">
7090  */
7091 /**
7092  * @class Roo.data.MemoryProxy
7093  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7094  * to the Reader when its load method is called.
7095  * @constructor
7096  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7097  */
7098 Roo.data.MemoryProxy = function(data){
7099     if (data.data) {
7100         data = data.data;
7101     }
7102     Roo.data.MemoryProxy.superclass.constructor.call(this);
7103     this.data = data;
7104 };
7105
7106 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7107     /**
7108      * Load data from the requested source (in this case an in-memory
7109      * data object passed to the constructor), read the data object into
7110      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7111      * process that block using the passed callback.
7112      * @param {Object} params This parameter is not used by the MemoryProxy class.
7113      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7114      * object into a block of Roo.data.Records.
7115      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7116      * The function must be passed <ul>
7117      * <li>The Record block object</li>
7118      * <li>The "arg" argument from the load function</li>
7119      * <li>A boolean success indicator</li>
7120      * </ul>
7121      * @param {Object} scope The scope in which to call the callback
7122      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7123      */
7124     load : function(params, reader, callback, scope, arg){
7125         params = params || {};
7126         var result;
7127         try {
7128             result = reader.readRecords(this.data);
7129         }catch(e){
7130             this.fireEvent("loadexception", this, arg, null, e);
7131             callback.call(scope, null, arg, false);
7132             return;
7133         }
7134         callback.call(scope, result, arg, true);
7135     },
7136     
7137     // private
7138     update : function(params, records){
7139         
7140     }
7141 });/*
7142  * Based on:
7143  * Ext JS Library 1.1.1
7144  * Copyright(c) 2006-2007, Ext JS, LLC.
7145  *
7146  * Originally Released Under LGPL - original licence link has changed is not relivant.
7147  *
7148  * Fork - LGPL
7149  * <script type="text/javascript">
7150  */
7151 /**
7152  * @class Roo.data.HttpProxy
7153  * @extends Roo.data.DataProxy
7154  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7155  * configured to reference a certain URL.<br><br>
7156  * <p>
7157  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7158  * from which the running page was served.<br><br>
7159  * <p>
7160  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7161  * <p>
7162  * Be aware that to enable the browser to parse an XML document, the server must set
7163  * the Content-Type header in the HTTP response to "text/xml".
7164  * @constructor
7165  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7166  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7167  * will be used to make the request.
7168  */
7169 Roo.data.HttpProxy = function(conn){
7170     Roo.data.HttpProxy.superclass.constructor.call(this);
7171     // is conn a conn config or a real conn?
7172     this.conn = conn;
7173     this.useAjax = !conn || !conn.events;
7174   
7175 };
7176
7177 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7178     // thse are take from connection...
7179     
7180     /**
7181      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7182      */
7183     /**
7184      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7185      * extra parameters to each request made by this object. (defaults to undefined)
7186      */
7187     /**
7188      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7189      *  to each request made by this object. (defaults to undefined)
7190      */
7191     /**
7192      * @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)
7193      */
7194     /**
7195      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7196      */
7197      /**
7198      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7199      * @type Boolean
7200      */
7201   
7202
7203     /**
7204      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7205      * @type Boolean
7206      */
7207     /**
7208      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7209      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7210      * a finer-grained basis than the DataProxy events.
7211      */
7212     getConnection : function(){
7213         return this.useAjax ? Roo.Ajax : this.conn;
7214     },
7215
7216     /**
7217      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7218      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7219      * process that block using the passed callback.
7220      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7221      * for the request to the remote server.
7222      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7223      * object into a block of Roo.data.Records.
7224      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7225      * The function must be passed <ul>
7226      * <li>The Record block object</li>
7227      * <li>The "arg" argument from the load function</li>
7228      * <li>A boolean success indicator</li>
7229      * </ul>
7230      * @param {Object} scope The scope in which to call the callback
7231      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7232      */
7233     load : function(params, reader, callback, scope, arg){
7234         if(this.fireEvent("beforeload", this, params) !== false){
7235             var  o = {
7236                 params : params || {},
7237                 request: {
7238                     callback : callback,
7239                     scope : scope,
7240                     arg : arg
7241                 },
7242                 reader: reader,
7243                 callback : this.loadResponse,
7244                 scope: this
7245             };
7246             if(this.useAjax){
7247                 Roo.applyIf(o, this.conn);
7248                 if(this.activeRequest){
7249                     Roo.Ajax.abort(this.activeRequest);
7250                 }
7251                 this.activeRequest = Roo.Ajax.request(o);
7252             }else{
7253                 this.conn.request(o);
7254             }
7255         }else{
7256             callback.call(scope||this, null, arg, false);
7257         }
7258     },
7259
7260     // private
7261     loadResponse : function(o, success, response){
7262         delete this.activeRequest;
7263         if(!success){
7264             this.fireEvent("loadexception", this, o, response);
7265             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7266             return;
7267         }
7268         var result;
7269         try {
7270             result = o.reader.read(response);
7271         }catch(e){
7272             this.fireEvent("loadexception", this, o, response, e);
7273             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7274             return;
7275         }
7276         
7277         this.fireEvent("load", this, o, o.request.arg);
7278         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7279     },
7280
7281     // private
7282     update : function(dataSet){
7283
7284     },
7285
7286     // private
7287     updateResponse : function(dataSet){
7288
7289     }
7290 });/*
7291  * Based on:
7292  * Ext JS Library 1.1.1
7293  * Copyright(c) 2006-2007, Ext JS, LLC.
7294  *
7295  * Originally Released Under LGPL - original licence link has changed is not relivant.
7296  *
7297  * Fork - LGPL
7298  * <script type="text/javascript">
7299  */
7300
7301 /**
7302  * @class Roo.data.ScriptTagProxy
7303  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7304  * other than the originating domain of the running page.<br><br>
7305  * <p>
7306  * <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
7307  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7308  * <p>
7309  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7310  * source code that is used as the source inside a &lt;script> tag.<br><br>
7311  * <p>
7312  * In order for the browser to process the returned data, the server must wrap the data object
7313  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7314  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7315  * depending on whether the callback name was passed:
7316  * <p>
7317  * <pre><code>
7318 boolean scriptTag = false;
7319 String cb = request.getParameter("callback");
7320 if (cb != null) {
7321     scriptTag = true;
7322     response.setContentType("text/javascript");
7323 } else {
7324     response.setContentType("application/x-json");
7325 }
7326 Writer out = response.getWriter();
7327 if (scriptTag) {
7328     out.write(cb + "(");
7329 }
7330 out.print(dataBlock.toJsonString());
7331 if (scriptTag) {
7332     out.write(");");
7333 }
7334 </pre></code>
7335  *
7336  * @constructor
7337  * @param {Object} config A configuration object.
7338  */
7339 Roo.data.ScriptTagProxy = function(config){
7340     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7341     Roo.apply(this, config);
7342     this.head = document.getElementsByTagName("head")[0];
7343 };
7344
7345 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7346
7347 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7348     /**
7349      * @cfg {String} url The URL from which to request the data object.
7350      */
7351     /**
7352      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7353      */
7354     timeout : 30000,
7355     /**
7356      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7357      * the server the name of the callback function set up by the load call to process the returned data object.
7358      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7359      * javascript output which calls this named function passing the data object as its only parameter.
7360      */
7361     callbackParam : "callback",
7362     /**
7363      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7364      * name to the request.
7365      */
7366     nocache : true,
7367
7368     /**
7369      * Load data from the configured URL, read the data object into
7370      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7371      * process that block using the passed callback.
7372      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7373      * for the request to the remote server.
7374      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7375      * object into a block of Roo.data.Records.
7376      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7377      * The function must be passed <ul>
7378      * <li>The Record block object</li>
7379      * <li>The "arg" argument from the load function</li>
7380      * <li>A boolean success indicator</li>
7381      * </ul>
7382      * @param {Object} scope The scope in which to call the callback
7383      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7384      */
7385     load : function(params, reader, callback, scope, arg){
7386         if(this.fireEvent("beforeload", this, params) !== false){
7387
7388             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7389
7390             var url = this.url;
7391             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7392             if(this.nocache){
7393                 url += "&_dc=" + (new Date().getTime());
7394             }
7395             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7396             var trans = {
7397                 id : transId,
7398                 cb : "stcCallback"+transId,
7399                 scriptId : "stcScript"+transId,
7400                 params : params,
7401                 arg : arg,
7402                 url : url,
7403                 callback : callback,
7404                 scope : scope,
7405                 reader : reader
7406             };
7407             var conn = this;
7408
7409             window[trans.cb] = function(o){
7410                 conn.handleResponse(o, trans);
7411             };
7412
7413             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7414
7415             if(this.autoAbort !== false){
7416                 this.abort();
7417             }
7418
7419             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7420
7421             var script = document.createElement("script");
7422             script.setAttribute("src", url);
7423             script.setAttribute("type", "text/javascript");
7424             script.setAttribute("id", trans.scriptId);
7425             this.head.appendChild(script);
7426
7427             this.trans = trans;
7428         }else{
7429             callback.call(scope||this, null, arg, false);
7430         }
7431     },
7432
7433     // private
7434     isLoading : function(){
7435         return this.trans ? true : false;
7436     },
7437
7438     /**
7439      * Abort the current server request.
7440      */
7441     abort : function(){
7442         if(this.isLoading()){
7443             this.destroyTrans(this.trans);
7444         }
7445     },
7446
7447     // private
7448     destroyTrans : function(trans, isLoaded){
7449         this.head.removeChild(document.getElementById(trans.scriptId));
7450         clearTimeout(trans.timeoutId);
7451         if(isLoaded){
7452             window[trans.cb] = undefined;
7453             try{
7454                 delete window[trans.cb];
7455             }catch(e){}
7456         }else{
7457             // if hasn't been loaded, wait for load to remove it to prevent script error
7458             window[trans.cb] = function(){
7459                 window[trans.cb] = undefined;
7460                 try{
7461                     delete window[trans.cb];
7462                 }catch(e){}
7463             };
7464         }
7465     },
7466
7467     // private
7468     handleResponse : function(o, trans){
7469         this.trans = false;
7470         this.destroyTrans(trans, true);
7471         var result;
7472         try {
7473             result = trans.reader.readRecords(o);
7474         }catch(e){
7475             this.fireEvent("loadexception", this, o, trans.arg, e);
7476             trans.callback.call(trans.scope||window, null, trans.arg, false);
7477             return;
7478         }
7479         this.fireEvent("load", this, o, trans.arg);
7480         trans.callback.call(trans.scope||window, result, trans.arg, true);
7481     },
7482
7483     // private
7484     handleFailure : function(trans){
7485         this.trans = false;
7486         this.destroyTrans(trans, false);
7487         this.fireEvent("loadexception", this, null, trans.arg);
7488         trans.callback.call(trans.scope||window, null, trans.arg, false);
7489     }
7490 });/*
7491  * Based on:
7492  * Ext JS Library 1.1.1
7493  * Copyright(c) 2006-2007, Ext JS, LLC.
7494  *
7495  * Originally Released Under LGPL - original licence link has changed is not relivant.
7496  *
7497  * Fork - LGPL
7498  * <script type="text/javascript">
7499  */
7500
7501 /**
7502  * @class Roo.data.JsonReader
7503  * @extends Roo.data.DataReader
7504  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7505  * based on mappings in a provided Roo.data.Record constructor.
7506  * 
7507  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7508  * in the reply previously. 
7509  * 
7510  * <p>
7511  * Example code:
7512  * <pre><code>
7513 var RecordDef = Roo.data.Record.create([
7514     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7515     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7516 ]);
7517 var myReader = new Roo.data.JsonReader({
7518     totalProperty: "results",    // The property which contains the total dataset size (optional)
7519     root: "rows",                // The property which contains an Array of row objects
7520     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7521 }, RecordDef);
7522 </code></pre>
7523  * <p>
7524  * This would consume a JSON file like this:
7525  * <pre><code>
7526 { 'results': 2, 'rows': [
7527     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7528     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7529 }
7530 </code></pre>
7531  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7532  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7533  * paged from the remote server.
7534  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7535  * @cfg {String} root name of the property which contains the Array of row objects.
7536  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7537  * @constructor
7538  * Create a new JsonReader
7539  * @param {Object} meta Metadata configuration options
7540  * @param {Object} recordType Either an Array of field definition objects,
7541  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7542  */
7543 Roo.data.JsonReader = function(meta, recordType){
7544     
7545     meta = meta || {};
7546     // set some defaults:
7547     Roo.applyIf(meta, {
7548         totalProperty: 'total',
7549         successProperty : 'success',
7550         root : 'data',
7551         id : 'id'
7552     });
7553     
7554     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7555 };
7556 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7557     
7558     /**
7559      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7560      * Used by Store query builder to append _requestMeta to params.
7561      * 
7562      */
7563     metaFromRemote : false,
7564     /**
7565      * This method is only used by a DataProxy which has retrieved data from a remote server.
7566      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7567      * @return {Object} data A data block which is used by an Roo.data.Store object as
7568      * a cache of Roo.data.Records.
7569      */
7570     read : function(response){
7571         var json = response.responseText;
7572        
7573         var o = /* eval:var:o */ eval("("+json+")");
7574         if(!o) {
7575             throw {message: "JsonReader.read: Json object not found"};
7576         }
7577         
7578         if(o.metaData){
7579             
7580             delete this.ef;
7581             this.metaFromRemote = true;
7582             this.meta = o.metaData;
7583             this.recordType = Roo.data.Record.create(o.metaData.fields);
7584             this.onMetaChange(this.meta, this.recordType, o);
7585         }
7586         return this.readRecords(o);
7587     },
7588
7589     // private function a store will implement
7590     onMetaChange : function(meta, recordType, o){
7591
7592     },
7593
7594     /**
7595          * @ignore
7596          */
7597     simpleAccess: function(obj, subsc) {
7598         return obj[subsc];
7599     },
7600
7601         /**
7602          * @ignore
7603          */
7604     getJsonAccessor: function(){
7605         var re = /[\[\.]/;
7606         return function(expr) {
7607             try {
7608                 return(re.test(expr))
7609                     ? new Function("obj", "return obj." + expr)
7610                     : function(obj){
7611                         return obj[expr];
7612                     };
7613             } catch(e){}
7614             return Roo.emptyFn;
7615         };
7616     }(),
7617
7618     /**
7619      * Create a data block containing Roo.data.Records from an XML document.
7620      * @param {Object} o An object which contains an Array of row objects in the property specified
7621      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7622      * which contains the total size of the dataset.
7623      * @return {Object} data A data block which is used by an Roo.data.Store object as
7624      * a cache of Roo.data.Records.
7625      */
7626     readRecords : function(o){
7627         /**
7628          * After any data loads, the raw JSON data is available for further custom processing.
7629          * @type Object
7630          */
7631         this.o = o;
7632         var s = this.meta, Record = this.recordType,
7633             f = Record.prototype.fields, fi = f.items, fl = f.length;
7634
7635 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7636         if (!this.ef) {
7637             if(s.totalProperty) {
7638                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7639                 }
7640                 if(s.successProperty) {
7641                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7642                 }
7643                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7644                 if (s.id) {
7645                         var g = this.getJsonAccessor(s.id);
7646                         this.getId = function(rec) {
7647                                 var r = g(rec);
7648                                 return (r === undefined || r === "") ? null : r;
7649                         };
7650                 } else {
7651                         this.getId = function(){return null;};
7652                 }
7653             this.ef = [];
7654             for(var jj = 0; jj < fl; jj++){
7655                 f = fi[jj];
7656                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7657                 this.ef[jj] = this.getJsonAccessor(map);
7658             }
7659         }
7660
7661         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7662         if(s.totalProperty){
7663             var vt = parseInt(this.getTotal(o), 10);
7664             if(!isNaN(vt)){
7665                 totalRecords = vt;
7666             }
7667         }
7668         if(s.successProperty){
7669             var vs = this.getSuccess(o);
7670             if(vs === false || vs === 'false'){
7671                 success = false;
7672             }
7673         }
7674         var records = [];
7675             for(var i = 0; i < c; i++){
7676                     var n = root[i];
7677                 var values = {};
7678                 var id = this.getId(n);
7679                 for(var j = 0; j < fl; j++){
7680                     f = fi[j];
7681                 var v = this.ef[j](n);
7682                 if (!f.convert) {
7683                     Roo.log('missing convert for ' + f.name);
7684                     Roo.log(f);
7685                     continue;
7686                 }
7687                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7688                 }
7689                 var record = new Record(values, id);
7690                 record.json = n;
7691                 records[i] = record;
7692             }
7693             return {
7694             raw : o,
7695                 success : success,
7696                 records : records,
7697                 totalRecords : totalRecords
7698             };
7699     }
7700 });/*
7701  * Based on:
7702  * Ext JS Library 1.1.1
7703  * Copyright(c) 2006-2007, Ext JS, LLC.
7704  *
7705  * Originally Released Under LGPL - original licence link has changed is not relivant.
7706  *
7707  * Fork - LGPL
7708  * <script type="text/javascript">
7709  */
7710
7711 /**
7712  * @class Roo.data.ArrayReader
7713  * @extends Roo.data.DataReader
7714  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7715  * Each element of that Array represents a row of data fields. The
7716  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7717  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7718  * <p>
7719  * Example code:.
7720  * <pre><code>
7721 var RecordDef = Roo.data.Record.create([
7722     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7723     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7724 ]);
7725 var myReader = new Roo.data.ArrayReader({
7726     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7727 }, RecordDef);
7728 </code></pre>
7729  * <p>
7730  * This would consume an Array like this:
7731  * <pre><code>
7732 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7733   </code></pre>
7734  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7735  * @constructor
7736  * Create a new JsonReader
7737  * @param {Object} meta Metadata configuration options.
7738  * @param {Object} recordType Either an Array of field definition objects
7739  * as specified to {@link Roo.data.Record#create},
7740  * or an {@link Roo.data.Record} object
7741  * created using {@link Roo.data.Record#create}.
7742  */
7743 Roo.data.ArrayReader = function(meta, recordType){
7744     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7745 };
7746
7747 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7748     /**
7749      * Create a data block containing Roo.data.Records from an XML document.
7750      * @param {Object} o An Array of row objects which represents the dataset.
7751      * @return {Object} data A data block which is used by an Roo.data.Store object as
7752      * a cache of Roo.data.Records.
7753      */
7754     readRecords : function(o){
7755         var sid = this.meta ? this.meta.id : null;
7756         var recordType = this.recordType, fields = recordType.prototype.fields;
7757         var records = [];
7758         var root = o;
7759             for(var i = 0; i < root.length; i++){
7760                     var n = root[i];
7761                 var values = {};
7762                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7763                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7764                 var f = fields.items[j];
7765                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7766                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7767                 v = f.convert(v);
7768                 values[f.name] = v;
7769             }
7770                 var record = new recordType(values, id);
7771                 record.json = n;
7772                 records[records.length] = record;
7773             }
7774             return {
7775                 records : records,
7776                 totalRecords : records.length
7777             };
7778     }
7779 });/*
7780  * - LGPL
7781  * * 
7782  */
7783
7784 /**
7785  * @class Roo.bootstrap.ComboBox
7786  * @extends Roo.bootstrap.TriggerField
7787  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7788  * @cfg {Boolean} append (true|false) default false
7789  * @constructor
7790  * Create a new ComboBox.
7791  * @param {Object} config Configuration options
7792  */
7793 Roo.bootstrap.ComboBox = function(config){
7794     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7795     this.addEvents({
7796         /**
7797          * @event expand
7798          * Fires when the dropdown list is expanded
7799              * @param {Roo.bootstrap.ComboBox} combo This combo box
7800              */
7801         'expand' : true,
7802         /**
7803          * @event collapse
7804          * Fires when the dropdown list is collapsed
7805              * @param {Roo.bootstrap.ComboBox} combo This combo box
7806              */
7807         'collapse' : true,
7808         /**
7809          * @event beforeselect
7810          * Fires before a list item is selected. Return false to cancel the selection.
7811              * @param {Roo.bootstrap.ComboBox} combo This combo box
7812              * @param {Roo.data.Record} record The data record returned from the underlying store
7813              * @param {Number} index The index of the selected item in the dropdown list
7814              */
7815         'beforeselect' : true,
7816         /**
7817          * @event select
7818          * Fires when a list item is selected
7819              * @param {Roo.bootstrap.ComboBox} combo This combo box
7820              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7821              * @param {Number} index The index of the selected item in the dropdown list
7822              */
7823         'select' : true,
7824         /**
7825          * @event beforequery
7826          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7827          * The event object passed has these properties:
7828              * @param {Roo.bootstrap.ComboBox} combo This combo box
7829              * @param {String} query The query
7830              * @param {Boolean} forceAll true to force "all" query
7831              * @param {Boolean} cancel true to cancel the query
7832              * @param {Object} e The query event object
7833              */
7834         'beforequery': true,
7835          /**
7836          * @event add
7837          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7838              * @param {Roo.bootstrap.ComboBox} combo This combo box
7839              */
7840         'add' : true,
7841         /**
7842          * @event edit
7843          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7844              * @param {Roo.bootstrap.ComboBox} combo This combo box
7845              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7846              */
7847         'edit' : true,
7848         /**
7849          * @event remove
7850          * Fires when the remove value from the combobox array
7851              * @param {Roo.bootstrap.ComboBox} combo This combo box
7852              */
7853         'remove' : true
7854         
7855     });
7856     
7857     
7858     this.selectedIndex = -1;
7859     if(this.mode == 'local'){
7860         if(config.queryDelay === undefined){
7861             this.queryDelay = 10;
7862         }
7863         if(config.minChars === undefined){
7864             this.minChars = 0;
7865         }
7866     }
7867 };
7868
7869 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7870      
7871     /**
7872      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7873      * rendering into an Roo.Editor, defaults to false)
7874      */
7875     /**
7876      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7877      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7878      */
7879     /**
7880      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7881      */
7882     /**
7883      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7884      * the dropdown list (defaults to undefined, with no header element)
7885      */
7886
7887      /**
7888      * @cfg {String/Roo.Template} tpl The template to use to render the output
7889      */
7890      
7891      /**
7892      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7893      */
7894     listWidth: undefined,
7895     /**
7896      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7897      * mode = 'remote' or 'text' if mode = 'local')
7898      */
7899     displayField: undefined,
7900     /**
7901      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7902      * mode = 'remote' or 'value' if mode = 'local'). 
7903      * Note: use of a valueField requires the user make a selection
7904      * in order for a value to be mapped.
7905      */
7906     valueField: undefined,
7907     
7908     
7909     /**
7910      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7911      * field's data value (defaults to the underlying DOM element's name)
7912      */
7913     hiddenName: undefined,
7914     /**
7915      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7916      */
7917     listClass: '',
7918     /**
7919      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7920      */
7921     selectedClass: 'active',
7922     
7923     /**
7924      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7925      */
7926     shadow:'sides',
7927     /**
7928      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7929      * anchor positions (defaults to 'tl-bl')
7930      */
7931     listAlign: 'tl-bl?',
7932     /**
7933      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7934      */
7935     maxHeight: 300,
7936     /**
7937      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7938      * query specified by the allQuery config option (defaults to 'query')
7939      */
7940     triggerAction: 'query',
7941     /**
7942      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7943      * (defaults to 4, does not apply if editable = false)
7944      */
7945     minChars : 4,
7946     /**
7947      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7948      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7949      */
7950     typeAhead: false,
7951     /**
7952      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7953      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7954      */
7955     queryDelay: 500,
7956     /**
7957      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7958      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7959      */
7960     pageSize: 0,
7961     /**
7962      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7963      * when editable = true (defaults to false)
7964      */
7965     selectOnFocus:false,
7966     /**
7967      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7968      */
7969     queryParam: 'query',
7970     /**
7971      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7972      * when mode = 'remote' (defaults to 'Loading...')
7973      */
7974     loadingText: 'Loading...',
7975     /**
7976      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7977      */
7978     resizable: false,
7979     /**
7980      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7981      */
7982     handleHeight : 8,
7983     /**
7984      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7985      * traditional select (defaults to true)
7986      */
7987     editable: true,
7988     /**
7989      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7990      */
7991     allQuery: '',
7992     /**
7993      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7994      */
7995     mode: 'remote',
7996     /**
7997      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7998      * listWidth has a higher value)
7999      */
8000     minListWidth : 70,
8001     /**
8002      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8003      * allow the user to set arbitrary text into the field (defaults to false)
8004      */
8005     forceSelection:false,
8006     /**
8007      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8008      * if typeAhead = true (defaults to 250)
8009      */
8010     typeAheadDelay : 250,
8011     /**
8012      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8013      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8014      */
8015     valueNotFoundText : undefined,
8016     /**
8017      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8018      */
8019     blockFocus : false,
8020     
8021     /**
8022      * @cfg {Boolean} disableClear Disable showing of clear button.
8023      */
8024     disableClear : false,
8025     /**
8026      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8027      */
8028     alwaysQuery : false,
8029     
8030     /**
8031      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8032      */
8033     multiple : false,
8034     
8035     //private
8036     addicon : false,
8037     editicon: false,
8038     
8039     page: 0,
8040     hasQuery: false,
8041     append: false,
8042     loadNext: false,
8043     item: [],
8044     
8045     // element that contains real text value.. (when hidden is used..)
8046      
8047     // private
8048     initEvents: function(){
8049         
8050         if (!this.store) {
8051             throw "can not find store for combo";
8052         }
8053         this.store = Roo.factory(this.store, Roo.data);
8054         
8055         
8056         
8057         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8058         
8059         
8060         if(this.hiddenName){
8061             
8062             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8063             
8064             this.hiddenField.dom.value =
8065                 this.hiddenValue !== undefined ? this.hiddenValue :
8066                 this.value !== undefined ? this.value : '';
8067
8068             // prevent input submission
8069             this.el.dom.removeAttribute('name');
8070             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8071              
8072              
8073         }
8074         //if(Roo.isGecko){
8075         //    this.el.dom.setAttribute('autocomplete', 'off');
8076         //}
8077
8078         var cls = 'x-combo-list';
8079         this.list = this.el.select('ul.dropdown-menu',true).first();
8080
8081         //this.list = new Roo.Layer({
8082         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8083         //});
8084         
8085         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8086         this.list.setWidth(lw);
8087         
8088         this.list.on('mouseover', this.onViewOver, this);
8089         this.list.on('mousemove', this.onViewMove, this);
8090         
8091         this.list.on('scroll', this.onViewScroll, this);
8092         
8093         /*
8094         this.list.swallowEvent('mousewheel');
8095         this.assetHeight = 0;
8096
8097         if(this.title){
8098             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8099             this.assetHeight += this.header.getHeight();
8100         }
8101
8102         this.innerList = this.list.createChild({cls:cls+'-inner'});
8103         this.innerList.on('mouseover', this.onViewOver, this);
8104         this.innerList.on('mousemove', this.onViewMove, this);
8105         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8106         
8107         if(this.allowBlank && !this.pageSize && !this.disableClear){
8108             this.footer = this.list.createChild({cls:cls+'-ft'});
8109             this.pageTb = new Roo.Toolbar(this.footer);
8110            
8111         }
8112         if(this.pageSize){
8113             this.footer = this.list.createChild({cls:cls+'-ft'});
8114             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8115                     {pageSize: this.pageSize});
8116             
8117         }
8118         
8119         if (this.pageTb && this.allowBlank && !this.disableClear) {
8120             var _this = this;
8121             this.pageTb.add(new Roo.Toolbar.Fill(), {
8122                 cls: 'x-btn-icon x-btn-clear',
8123                 text: '&#160;',
8124                 handler: function()
8125                 {
8126                     _this.collapse();
8127                     _this.clearValue();
8128                     _this.onSelect(false, -1);
8129                 }
8130             });
8131         }
8132         if (this.footer) {
8133             this.assetHeight += this.footer.getHeight();
8134         }
8135         */
8136             
8137         if(!this.tpl){
8138             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8139         }
8140
8141         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8142             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8143         });
8144         //this.view.wrapEl.setDisplayed(false);
8145         this.view.on('click', this.onViewClick, this);
8146         
8147         
8148         
8149         this.store.on('beforeload', this.onBeforeLoad, this);
8150         this.store.on('load', this.onLoad, this);
8151         this.store.on('loadexception', this.onLoadException, this);
8152         /*
8153         if(this.resizable){
8154             this.resizer = new Roo.Resizable(this.list,  {
8155                pinned:true, handles:'se'
8156             });
8157             this.resizer.on('resize', function(r, w, h){
8158                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8159                 this.listWidth = w;
8160                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8161                 this.restrictHeight();
8162             }, this);
8163             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8164         }
8165         */
8166         if(!this.editable){
8167             this.editable = true;
8168             this.setEditable(false);
8169         }
8170         
8171         /*
8172         
8173         if (typeof(this.events.add.listeners) != 'undefined') {
8174             
8175             this.addicon = this.wrap.createChild(
8176                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8177        
8178             this.addicon.on('click', function(e) {
8179                 this.fireEvent('add', this);
8180             }, this);
8181         }
8182         if (typeof(this.events.edit.listeners) != 'undefined') {
8183             
8184             this.editicon = this.wrap.createChild(
8185                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8186             if (this.addicon) {
8187                 this.editicon.setStyle('margin-left', '40px');
8188             }
8189             this.editicon.on('click', function(e) {
8190                 
8191                 // we fire even  if inothing is selected..
8192                 this.fireEvent('edit', this, this.lastData );
8193                 
8194             }, this);
8195         }
8196         */
8197         
8198         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8199             "up" : function(e){
8200                 this.inKeyMode = true;
8201                 this.selectPrev();
8202             },
8203
8204             "down" : function(e){
8205                 if(!this.isExpanded()){
8206                     this.onTriggerClick();
8207                 }else{
8208                     this.inKeyMode = true;
8209                     this.selectNext();
8210                 }
8211             },
8212
8213             "enter" : function(e){
8214                 this.onViewClick();
8215                 //return true;
8216             },
8217
8218             "esc" : function(e){
8219                 this.collapse();
8220             },
8221
8222             "tab" : function(e){
8223                 this.collapse();
8224                 
8225                 if(this.fireEvent("specialkey", this, e)){
8226                     this.onViewClick(false);
8227                 }
8228                 
8229                 return true;
8230             },
8231
8232             scope : this,
8233
8234             doRelay : function(foo, bar, hname){
8235                 if(hname == 'down' || this.scope.isExpanded()){
8236                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8237                 }
8238                 return true;
8239             },
8240
8241             forceKeyDown: true
8242         });
8243         
8244         
8245         this.queryDelay = Math.max(this.queryDelay || 10,
8246                 this.mode == 'local' ? 10 : 250);
8247         
8248         
8249         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8250         
8251         if(this.typeAhead){
8252             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8253         }
8254         if(this.editable !== false){
8255             this.inputEl().on("keyup", this.onKeyUp, this);
8256         }
8257         if(this.forceSelection){
8258             this.on('blur', this.doForce, this);
8259         }
8260         
8261         if(this.multiple){
8262             this.choices = this.el.select('ul.select2-choices', true).first();
8263             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8264         }
8265     },
8266
8267     onDestroy : function(){
8268         if(this.view){
8269             this.view.setStore(null);
8270             this.view.el.removeAllListeners();
8271             this.view.el.remove();
8272             this.view.purgeListeners();
8273         }
8274         if(this.list){
8275             this.list.dom.innerHTML  = '';
8276         }
8277         if(this.store){
8278             this.store.un('beforeload', this.onBeforeLoad, this);
8279             this.store.un('load', this.onLoad, this);
8280             this.store.un('loadexception', this.onLoadException, this);
8281         }
8282         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8283     },
8284
8285     // private
8286     fireKey : function(e){
8287         if(e.isNavKeyPress() && !this.list.isVisible()){
8288             this.fireEvent("specialkey", this, e);
8289         }
8290     },
8291
8292     // private
8293     onResize: function(w, h){
8294 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8295 //        
8296 //        if(typeof w != 'number'){
8297 //            // we do not handle it!?!?
8298 //            return;
8299 //        }
8300 //        var tw = this.trigger.getWidth();
8301 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8302 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8303 //        var x = w - tw;
8304 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8305 //            
8306 //        //this.trigger.setStyle('left', x+'px');
8307 //        
8308 //        if(this.list && this.listWidth === undefined){
8309 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8310 //            this.list.setWidth(lw);
8311 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8312 //        }
8313         
8314     
8315         
8316     },
8317
8318     /**
8319      * Allow or prevent the user from directly editing the field text.  If false is passed,
8320      * the user will only be able to select from the items defined in the dropdown list.  This method
8321      * is the runtime equivalent of setting the 'editable' config option at config time.
8322      * @param {Boolean} value True to allow the user to directly edit the field text
8323      */
8324     setEditable : function(value){
8325         if(value == this.editable){
8326             return;
8327         }
8328         this.editable = value;
8329         if(!value){
8330             this.inputEl().dom.setAttribute('readOnly', true);
8331             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8332             this.inputEl().addClass('x-combo-noedit');
8333         }else{
8334             this.inputEl().dom.setAttribute('readOnly', false);
8335             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8336             this.inputEl().removeClass('x-combo-noedit');
8337         }
8338     },
8339
8340     // private
8341     
8342     onBeforeLoad : function(combo,opts){
8343         if(!this.hasFocus){
8344             return;
8345         }
8346          if (!opts.add) {
8347             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8348          }
8349         this.restrictHeight();
8350         this.selectedIndex = -1;
8351     },
8352
8353     // private
8354     onLoad : function(){
8355         
8356         this.hasQuery = false;
8357         
8358         if(!this.hasFocus){
8359             return;
8360         }
8361         
8362         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8363             this.loading.hide();
8364         }
8365         
8366         if(this.store.getCount() > 0){
8367             this.expand();
8368             this.restrictHeight();
8369             if(this.lastQuery == this.allQuery){
8370                 if(this.editable){
8371                     this.inputEl().dom.select();
8372                 }
8373                 if(!this.selectByValue(this.value, true)){
8374                     this.select(0, true);
8375                 }
8376             }else{
8377                 this.selectNext();
8378                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8379                     this.taTask.delay(this.typeAheadDelay);
8380                 }
8381             }
8382         }else{
8383             this.onEmptyResults();
8384         }
8385         
8386         //this.el.focus();
8387     },
8388     // private
8389     onLoadException : function()
8390     {
8391         this.hasQuery = false;
8392         
8393         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8394             this.loading.hide();
8395         }
8396         
8397         this.collapse();
8398         Roo.log(this.store.reader.jsonData);
8399         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8400             // fixme
8401             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8402         }
8403         
8404         
8405     },
8406     // private
8407     onTypeAhead : function(){
8408         if(this.store.getCount() > 0){
8409             var r = this.store.getAt(0);
8410             var newValue = r.data[this.displayField];
8411             var len = newValue.length;
8412             var selStart = this.getRawValue().length;
8413             
8414             if(selStart != len){
8415                 this.setRawValue(newValue);
8416                 this.selectText(selStart, newValue.length);
8417             }
8418         }
8419     },
8420
8421     // private
8422     onSelect : function(record, index){
8423         
8424         if(this.fireEvent('beforeselect', this, record, index) !== false){
8425         
8426             this.setFromData(index > -1 ? record.data : false);
8427             
8428             this.collapse();
8429             this.fireEvent('select', this, record, index);
8430         }
8431     },
8432
8433     /**
8434      * Returns the currently selected field value or empty string if no value is set.
8435      * @return {String} value The selected value
8436      */
8437     getValue : function(){
8438         
8439         if(this.multiple){
8440             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8441         }
8442         
8443         if(this.valueField){
8444             return typeof this.value != 'undefined' ? this.value : '';
8445         }else{
8446             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8447         }
8448     },
8449
8450     /**
8451      * Clears any text/value currently set in the field
8452      */
8453     clearValue : function(){
8454         if(this.hiddenField){
8455             this.hiddenField.dom.value = '';
8456         }
8457         this.value = '';
8458         this.setRawValue('');
8459         this.lastSelectionText = '';
8460         
8461     },
8462
8463     /**
8464      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8465      * will be displayed in the field.  If the value does not match the data value of an existing item,
8466      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8467      * Otherwise the field will be blank (although the value will still be set).
8468      * @param {String} value The value to match
8469      */
8470     setValue : function(v){
8471         if(this.multiple){
8472             this.syncValue();
8473             return;
8474         }
8475         
8476         var text = v;
8477         if(this.valueField){
8478             var r = this.findRecord(this.valueField, v);
8479             if(r){
8480                 text = r.data[this.displayField];
8481             }else if(this.valueNotFoundText !== undefined){
8482                 text = this.valueNotFoundText;
8483             }
8484         }
8485         this.lastSelectionText = text;
8486         if(this.hiddenField){
8487             this.hiddenField.dom.value = v;
8488         }
8489         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8490         this.value = v;
8491     },
8492     /**
8493      * @property {Object} the last set data for the element
8494      */
8495     
8496     lastData : false,
8497     /**
8498      * Sets the value of the field based on a object which is related to the record format for the store.
8499      * @param {Object} value the value to set as. or false on reset?
8500      */
8501     setFromData : function(o){
8502         
8503         if(this.multiple){
8504             this.addItem(o);
8505             return;
8506         }
8507             
8508         var dv = ''; // display value
8509         var vv = ''; // value value..
8510         this.lastData = o;
8511         if (this.displayField) {
8512             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8513         } else {
8514             // this is an error condition!!!
8515             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8516         }
8517         
8518         if(this.valueField){
8519             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8520         }
8521         
8522         if(this.hiddenField){
8523             this.hiddenField.dom.value = vv;
8524             
8525             this.lastSelectionText = dv;
8526             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8527             this.value = vv;
8528             return;
8529         }
8530         // no hidden field.. - we store the value in 'value', but still display
8531         // display field!!!!
8532         this.lastSelectionText = dv;
8533         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8534         this.value = vv;
8535         
8536         
8537     },
8538     // private
8539     reset : function(){
8540         // overridden so that last data is reset..
8541         this.setValue(this.originalValue);
8542         this.clearInvalid();
8543         this.lastData = false;
8544         if (this.view) {
8545             this.view.clearSelections();
8546         }
8547     },
8548     // private
8549     findRecord : function(prop, value){
8550         var record;
8551         if(this.store.getCount() > 0){
8552             this.store.each(function(r){
8553                 if(r.data[prop] == value){
8554                     record = r;
8555                     return false;
8556                 }
8557                 return true;
8558             });
8559         }
8560         return record;
8561     },
8562     
8563     getName: function()
8564     {
8565         // returns hidden if it's set..
8566         if (!this.rendered) {return ''};
8567         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8568         
8569     },
8570     // private
8571     onViewMove : function(e, t){
8572         this.inKeyMode = false;
8573     },
8574
8575     // private
8576     onViewOver : function(e, t){
8577         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8578             return;
8579         }
8580         var item = this.view.findItemFromChild(t);
8581         if(item){
8582             var index = this.view.indexOf(item);
8583             this.select(index, false);
8584         }
8585     },
8586
8587     // private
8588     onViewClick : function(doFocus)
8589     {
8590         var index = this.view.getSelectedIndexes()[0];
8591         var r = this.store.getAt(index);
8592         if(r){
8593             this.onSelect(r, index);
8594         }
8595         if(doFocus !== false && !this.blockFocus){
8596             this.inputEl().focus();
8597         }
8598     },
8599
8600     // private
8601     restrictHeight : function(){
8602         //this.innerList.dom.style.height = '';
8603         //var inner = this.innerList.dom;
8604         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8605         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8606         //this.list.beginUpdate();
8607         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8608         this.list.alignTo(this.inputEl(), this.listAlign);
8609         //this.list.endUpdate();
8610     },
8611
8612     // private
8613     onEmptyResults : function(){
8614         this.collapse();
8615     },
8616
8617     /**
8618      * Returns true if the dropdown list is expanded, else false.
8619      */
8620     isExpanded : function(){
8621         return this.list.isVisible();
8622     },
8623
8624     /**
8625      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8626      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8627      * @param {String} value The data value of the item to select
8628      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8629      * selected item if it is not currently in view (defaults to true)
8630      * @return {Boolean} True if the value matched an item in the list, else false
8631      */
8632     selectByValue : function(v, scrollIntoView){
8633         if(v !== undefined && v !== null){
8634             var r = this.findRecord(this.valueField || this.displayField, v);
8635             if(r){
8636                 this.select(this.store.indexOf(r), scrollIntoView);
8637                 return true;
8638             }
8639         }
8640         return false;
8641     },
8642
8643     /**
8644      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8645      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8646      * @param {Number} index The zero-based index of the list item to select
8647      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8648      * selected item if it is not currently in view (defaults to true)
8649      */
8650     select : function(index, scrollIntoView){
8651         this.selectedIndex = index;
8652         this.view.select(index);
8653         if(scrollIntoView !== false){
8654             var el = this.view.getNode(index);
8655             if(el){
8656                 //this.innerList.scrollChildIntoView(el, false);
8657                 
8658             }
8659         }
8660     },
8661
8662     // private
8663     selectNext : function(){
8664         var ct = this.store.getCount();
8665         if(ct > 0){
8666             if(this.selectedIndex == -1){
8667                 this.select(0);
8668             }else if(this.selectedIndex < ct-1){
8669                 this.select(this.selectedIndex+1);
8670             }
8671         }
8672     },
8673
8674     // private
8675     selectPrev : function(){
8676         var ct = this.store.getCount();
8677         if(ct > 0){
8678             if(this.selectedIndex == -1){
8679                 this.select(0);
8680             }else if(this.selectedIndex != 0){
8681                 this.select(this.selectedIndex-1);
8682             }
8683         }
8684     },
8685
8686     // private
8687     onKeyUp : function(e){
8688         if(this.editable !== false && !e.isSpecialKey()){
8689             this.lastKey = e.getKey();
8690             this.dqTask.delay(this.queryDelay);
8691         }
8692     },
8693
8694     // private
8695     validateBlur : function(){
8696         return !this.list || !this.list.isVisible();   
8697     },
8698
8699     // private
8700     initQuery : function(){
8701         this.doQuery(this.getRawValue());
8702     },
8703
8704     // private
8705     doForce : function(){
8706         if(this.el.dom.value.length > 0){
8707             this.el.dom.value =
8708                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8709              
8710         }
8711     },
8712
8713     /**
8714      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8715      * query allowing the query action to be canceled if needed.
8716      * @param {String} query The SQL query to execute
8717      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8718      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8719      * saved in the current store (defaults to false)
8720      */
8721     doQuery : function(q, forceAll){
8722         
8723         if(q === undefined || q === null){
8724             q = '';
8725         }
8726         var qe = {
8727             query: q,
8728             forceAll: forceAll,
8729             combo: this,
8730             cancel:false
8731         };
8732         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8733             return false;
8734         }
8735         q = qe.query;
8736         
8737         forceAll = qe.forceAll;
8738         if(forceAll === true || (q.length >= this.minChars)){
8739             
8740             this.hasQuery = true;
8741             
8742             if(this.lastQuery != q || this.alwaysQuery){
8743                 this.lastQuery = q;
8744                 if(this.mode == 'local'){
8745                     this.selectedIndex = -1;
8746                     if(forceAll){
8747                         this.store.clearFilter();
8748                     }else{
8749                         this.store.filter(this.displayField, q);
8750                     }
8751                     this.onLoad();
8752                 }else{
8753                     this.store.baseParams[this.queryParam] = q;
8754                     
8755                     var options = {params : this.getParams(q)};
8756                     
8757                     if(this.loadNext){
8758                         options.add = true;
8759                         options.params.start = this.page * this.pageSize;
8760                     }
8761                     
8762                     this.store.load(options);
8763                     this.expand();
8764                 }
8765             }else{
8766                 this.selectedIndex = -1;
8767                 this.onLoad();   
8768             }
8769         }
8770         
8771         this.loadNext = false;
8772     },
8773
8774     // private
8775     getParams : function(q){
8776         var p = {};
8777         //p[this.queryParam] = q;
8778         
8779         if(this.pageSize){
8780             p.start = 0;
8781             p.limit = this.pageSize;
8782         }
8783         return p;
8784     },
8785
8786     /**
8787      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8788      */
8789     collapse : function(){
8790         if(!this.isExpanded()){
8791             return;
8792         }
8793         
8794         this.list.hide();
8795         Roo.get(document).un('mousedown', this.collapseIf, this);
8796         Roo.get(document).un('mousewheel', this.collapseIf, this);
8797         if (!this.editable) {
8798             Roo.get(document).un('keydown', this.listKeyPress, this);
8799         }
8800         this.fireEvent('collapse', this);
8801     },
8802
8803     // private
8804     collapseIf : function(e){
8805         var in_combo  = e.within(this.el);
8806         var in_list =  e.within(this.list);
8807         
8808         if (in_combo || in_list) {
8809             //e.stopPropagation();
8810             return;
8811         }
8812
8813         this.collapse();
8814         
8815     },
8816
8817     /**
8818      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8819      */
8820     expand : function(){
8821        
8822         if(this.isExpanded() || !this.hasFocus){
8823             return;
8824         }
8825          Roo.log('expand');
8826         this.list.alignTo(this.inputEl(), this.listAlign);
8827         this.list.show();
8828         Roo.get(document).on('mousedown', this.collapseIf, this);
8829         Roo.get(document).on('mousewheel', this.collapseIf, this);
8830         if (!this.editable) {
8831             Roo.get(document).on('keydown', this.listKeyPress, this);
8832         }
8833         
8834         this.fireEvent('expand', this);
8835     },
8836
8837     // private
8838     // Implements the default empty TriggerField.onTriggerClick function
8839     onTriggerClick : function()
8840     {
8841         Roo.log('trigger click');
8842         
8843         if(this.disabled){
8844             return;
8845         }
8846         
8847         this.page = 0;
8848         this.loadNext = false;
8849         
8850         if(this.isExpanded()){
8851             this.collapse();
8852             if (!this.blockFocus) {
8853                 this.inputEl().focus();
8854             }
8855             
8856         }else {
8857             this.hasFocus = true;
8858             if(this.triggerAction == 'all') {
8859                 this.doQuery(this.allQuery, true);
8860             } else {
8861                 this.doQuery(this.getRawValue());
8862             }
8863             if (!this.blockFocus) {
8864                 this.inputEl().focus();
8865             }
8866         }
8867     },
8868     listKeyPress : function(e)
8869     {
8870         //Roo.log('listkeypress');
8871         // scroll to first matching element based on key pres..
8872         if (e.isSpecialKey()) {
8873             return false;
8874         }
8875         var k = String.fromCharCode(e.getKey()).toUpperCase();
8876         //Roo.log(k);
8877         var match  = false;
8878         var csel = this.view.getSelectedNodes();
8879         var cselitem = false;
8880         if (csel.length) {
8881             var ix = this.view.indexOf(csel[0]);
8882             cselitem  = this.store.getAt(ix);
8883             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8884                 cselitem = false;
8885             }
8886             
8887         }
8888         
8889         this.store.each(function(v) { 
8890             if (cselitem) {
8891                 // start at existing selection.
8892                 if (cselitem.id == v.id) {
8893                     cselitem = false;
8894                 }
8895                 return true;
8896             }
8897                 
8898             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8899                 match = this.store.indexOf(v);
8900                 return false;
8901             }
8902             return true;
8903         }, this);
8904         
8905         if (match === false) {
8906             return true; // no more action?
8907         }
8908         // scroll to?
8909         this.view.select(match);
8910         var sn = Roo.get(this.view.getSelectedNodes()[0])
8911         //sn.scrollIntoView(sn.dom.parentNode, false);
8912     },
8913     
8914     onViewScroll : function(e, t){
8915         
8916         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8917             return;
8918         }
8919         
8920         this.hasQuery = true;
8921         
8922         this.loading = this.list.select('.loading', true).first();
8923         
8924         if(this.loading === null){
8925             this.list.createChild({
8926                 tag: 'div',
8927                 cls: 'loading select2-more-results select2-active',
8928                 html: 'Loading more results...'
8929             })
8930             
8931             this.loading = this.list.select('.loading', true).first();
8932             
8933             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8934             
8935             this.loading.hide();
8936         }
8937         
8938         this.loading.show();
8939         
8940         var _combo = this;
8941         
8942         this.page++;
8943         this.loadNext = true;
8944         
8945         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8946         
8947         return;
8948     },
8949     
8950     addItem : function(o)
8951     {   
8952         var dv = ''; // display value
8953         
8954         if (this.displayField) {
8955             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8956         } else {
8957             // this is an error condition!!!
8958             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8959         }
8960         
8961         if(!dv.length){
8962             return;
8963         }
8964         
8965         var choice = this.choices.createChild({
8966             tag: 'li',
8967             cls: 'select2-search-choice',
8968             cn: [
8969                 {
8970                     tag: 'div',
8971                     html: dv
8972                 },
8973                 {
8974                     tag: 'a',
8975                     href: '#',
8976                     cls: 'select2-search-choice-close',
8977                     tabindex: '-1'
8978                 }
8979             ]
8980             
8981         }, this.searchField);
8982         
8983         var close = choice.select('a.select2-search-choice-close', true).first()
8984         
8985         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8986         
8987         this.item.push(o);
8988         this.lastData = o;
8989         
8990         this.syncValue();
8991         
8992         this.inputEl().dom.value = '';
8993         
8994     },
8995     
8996     onRemoveItem : function(e, _self, o)
8997     {
8998         Roo.log('remove item');
8999         var index = this.item.indexOf(o.data) * 1;
9000         
9001         if( index < 0){
9002             Roo.log('not this item?!');
9003             return;
9004         }
9005         
9006         this.item.splice(index, 1);
9007         o.item.remove();
9008         
9009         this.syncValue();
9010         
9011         this.fireEvent('remove', this);
9012         
9013     },
9014     
9015     syncValue : function()
9016     {
9017         if(!this.item.length){
9018             this.clearValue();
9019             return;
9020         }
9021             
9022         var value = [];
9023         var _this = this;
9024         Roo.each(this.item, function(i){
9025             if(_this.valueField){
9026                 value.push(i[_this.valueField]);
9027                 return;
9028             }
9029
9030             value.push(i);
9031         });
9032
9033         this.value = value.join(',');
9034
9035         if(this.hiddenField){
9036             this.hiddenField.dom.value = this.value;
9037         }
9038     },
9039     
9040     clearItem : function()
9041     {
9042         if(!this.multiple){
9043             return;
9044         }
9045         
9046         this.item = [];
9047         
9048         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9049            c.remove();
9050         });
9051         
9052         this.syncValue();
9053     }
9054     
9055     
9056
9057     /** 
9058     * @cfg {Boolean} grow 
9059     * @hide 
9060     */
9061     /** 
9062     * @cfg {Number} growMin 
9063     * @hide 
9064     */
9065     /** 
9066     * @cfg {Number} growMax 
9067     * @hide 
9068     */
9069     /**
9070      * @hide
9071      * @method autoSize
9072      */
9073 });
9074 /*
9075  * Based on:
9076  * Ext JS Library 1.1.1
9077  * Copyright(c) 2006-2007, Ext JS, LLC.
9078  *
9079  * Originally Released Under LGPL - original licence link has changed is not relivant.
9080  *
9081  * Fork - LGPL
9082  * <script type="text/javascript">
9083  */
9084
9085 /**
9086  * @class Roo.View
9087  * @extends Roo.util.Observable
9088  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9089  * This class also supports single and multi selection modes. <br>
9090  * Create a data model bound view:
9091  <pre><code>
9092  var store = new Roo.data.Store(...);
9093
9094  var view = new Roo.View({
9095     el : "my-element",
9096     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9097  
9098     singleSelect: true,
9099     selectedClass: "ydataview-selected",
9100     store: store
9101  });
9102
9103  // listen for node click?
9104  view.on("click", function(vw, index, node, e){
9105  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9106  });
9107
9108  // load XML data
9109  dataModel.load("foobar.xml");
9110  </code></pre>
9111  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9112  * <br><br>
9113  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9114  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9115  * 
9116  * Note: old style constructor is still suported (container, template, config)
9117  * 
9118  * @constructor
9119  * Create a new View
9120  * @param {Object} config The config object
9121  * 
9122  */
9123 Roo.View = function(config, depreciated_tpl, depreciated_config){
9124     
9125     if (typeof(depreciated_tpl) == 'undefined') {
9126         // new way.. - universal constructor.
9127         Roo.apply(this, config);
9128         this.el  = Roo.get(this.el);
9129     } else {
9130         // old format..
9131         this.el  = Roo.get(config);
9132         this.tpl = depreciated_tpl;
9133         Roo.apply(this, depreciated_config);
9134     }
9135     this.wrapEl  = this.el.wrap().wrap();
9136     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9137     
9138     
9139     if(typeof(this.tpl) == "string"){
9140         this.tpl = new Roo.Template(this.tpl);
9141     } else {
9142         // support xtype ctors..
9143         this.tpl = new Roo.factory(this.tpl, Roo);
9144     }
9145     
9146     
9147     this.tpl.compile();
9148    
9149   
9150     
9151      
9152     /** @private */
9153     this.addEvents({
9154         /**
9155          * @event beforeclick
9156          * Fires before a click is processed. Returns false to cancel the default action.
9157          * @param {Roo.View} this
9158          * @param {Number} index The index of the target node
9159          * @param {HTMLElement} node The target node
9160          * @param {Roo.EventObject} e The raw event object
9161          */
9162             "beforeclick" : true,
9163         /**
9164          * @event click
9165          * Fires when a template node is clicked.
9166          * @param {Roo.View} this
9167          * @param {Number} index The index of the target node
9168          * @param {HTMLElement} node The target node
9169          * @param {Roo.EventObject} e The raw event object
9170          */
9171             "click" : true,
9172         /**
9173          * @event dblclick
9174          * Fires when a template node is double clicked.
9175          * @param {Roo.View} this
9176          * @param {Number} index The index of the target node
9177          * @param {HTMLElement} node The target node
9178          * @param {Roo.EventObject} e The raw event object
9179          */
9180             "dblclick" : true,
9181         /**
9182          * @event contextmenu
9183          * Fires when a template node is right clicked.
9184          * @param {Roo.View} this
9185          * @param {Number} index The index of the target node
9186          * @param {HTMLElement} node The target node
9187          * @param {Roo.EventObject} e The raw event object
9188          */
9189             "contextmenu" : true,
9190         /**
9191          * @event selectionchange
9192          * Fires when the selected nodes change.
9193          * @param {Roo.View} this
9194          * @param {Array} selections Array of the selected nodes
9195          */
9196             "selectionchange" : true,
9197     
9198         /**
9199          * @event beforeselect
9200          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9201          * @param {Roo.View} this
9202          * @param {HTMLElement} node The node to be selected
9203          * @param {Array} selections Array of currently selected nodes
9204          */
9205             "beforeselect" : true,
9206         /**
9207          * @event preparedata
9208          * Fires on every row to render, to allow you to change the data.
9209          * @param {Roo.View} this
9210          * @param {Object} data to be rendered (change this)
9211          */
9212           "preparedata" : true
9213           
9214           
9215         });
9216
9217
9218
9219     this.el.on({
9220         "click": this.onClick,
9221         "dblclick": this.onDblClick,
9222         "contextmenu": this.onContextMenu,
9223         scope:this
9224     });
9225
9226     this.selections = [];
9227     this.nodes = [];
9228     this.cmp = new Roo.CompositeElementLite([]);
9229     if(this.store){
9230         this.store = Roo.factory(this.store, Roo.data);
9231         this.setStore(this.store, true);
9232     }
9233     
9234     if ( this.footer && this.footer.xtype) {
9235            
9236          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9237         
9238         this.footer.dataSource = this.store
9239         this.footer.container = fctr;
9240         this.footer = Roo.factory(this.footer, Roo);
9241         fctr.insertFirst(this.el);
9242         
9243         // this is a bit insane - as the paging toolbar seems to detach the el..
9244 //        dom.parentNode.parentNode.parentNode
9245          // they get detached?
9246     }
9247     
9248     
9249     Roo.View.superclass.constructor.call(this);
9250     
9251     
9252 };
9253
9254 Roo.extend(Roo.View, Roo.util.Observable, {
9255     
9256      /**
9257      * @cfg {Roo.data.Store} store Data store to load data from.
9258      */
9259     store : false,
9260     
9261     /**
9262      * @cfg {String|Roo.Element} el The container element.
9263      */
9264     el : '',
9265     
9266     /**
9267      * @cfg {String|Roo.Template} tpl The template used by this View 
9268      */
9269     tpl : false,
9270     /**
9271      * @cfg {String} dataName the named area of the template to use as the data area
9272      *                          Works with domtemplates roo-name="name"
9273      */
9274     dataName: false,
9275     /**
9276      * @cfg {String} selectedClass The css class to add to selected nodes
9277      */
9278     selectedClass : "x-view-selected",
9279      /**
9280      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9281      */
9282     emptyText : "",
9283     
9284     /**
9285      * @cfg {String} text to display on mask (default Loading)
9286      */
9287     mask : false,
9288     /**
9289      * @cfg {Boolean} multiSelect Allow multiple selection
9290      */
9291     multiSelect : false,
9292     /**
9293      * @cfg {Boolean} singleSelect Allow single selection
9294      */
9295     singleSelect:  false,
9296     
9297     /**
9298      * @cfg {Boolean} toggleSelect - selecting 
9299      */
9300     toggleSelect : false,
9301     
9302     /**
9303      * Returns the element this view is bound to.
9304      * @return {Roo.Element}
9305      */
9306     getEl : function(){
9307         return this.wrapEl;
9308     },
9309     
9310     
9311
9312     /**
9313      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9314      */
9315     refresh : function(){
9316         Roo.log('refresh');
9317         var t = this.tpl;
9318         
9319         // if we are using something like 'domtemplate', then
9320         // the what gets used is:
9321         // t.applySubtemplate(NAME, data, wrapping data..)
9322         // the outer template then get' applied with
9323         //     the store 'extra data'
9324         // and the body get's added to the
9325         //      roo-name="data" node?
9326         //      <span class='roo-tpl-{name}'></span> ?????
9327         
9328         
9329         
9330         this.clearSelections();
9331         this.el.update("");
9332         var html = [];
9333         var records = this.store.getRange();
9334         if(records.length < 1) {
9335             
9336             // is this valid??  = should it render a template??
9337             
9338             this.el.update(this.emptyText);
9339             return;
9340         }
9341         var el = this.el;
9342         if (this.dataName) {
9343             this.el.update(t.apply(this.store.meta)); //????
9344             el = this.el.child('.roo-tpl-' + this.dataName);
9345         }
9346         
9347         for(var i = 0, len = records.length; i < len; i++){
9348             var data = this.prepareData(records[i].data, i, records[i]);
9349             this.fireEvent("preparedata", this, data, i, records[i]);
9350             html[html.length] = Roo.util.Format.trim(
9351                 this.dataName ?
9352                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9353                     t.apply(data)
9354             );
9355         }
9356         
9357         
9358         
9359         el.update(html.join(""));
9360         this.nodes = el.dom.childNodes;
9361         this.updateIndexes(0);
9362     },
9363     
9364
9365     /**
9366      * Function to override to reformat the data that is sent to
9367      * the template for each node.
9368      * DEPRICATED - use the preparedata event handler.
9369      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9370      * a JSON object for an UpdateManager bound view).
9371      */
9372     prepareData : function(data, index, record)
9373     {
9374         this.fireEvent("preparedata", this, data, index, record);
9375         return data;
9376     },
9377
9378     onUpdate : function(ds, record){
9379          Roo.log('on update');   
9380         this.clearSelections();
9381         var index = this.store.indexOf(record);
9382         var n = this.nodes[index];
9383         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9384         n.parentNode.removeChild(n);
9385         this.updateIndexes(index, index);
9386     },
9387
9388     
9389     
9390 // --------- FIXME     
9391     onAdd : function(ds, records, index)
9392     {
9393         Roo.log(['on Add', ds, records, index] );        
9394         this.clearSelections();
9395         if(this.nodes.length == 0){
9396             this.refresh();
9397             return;
9398         }
9399         var n = this.nodes[index];
9400         for(var i = 0, len = records.length; i < len; i++){
9401             var d = this.prepareData(records[i].data, i, records[i]);
9402             if(n){
9403                 this.tpl.insertBefore(n, d);
9404             }else{
9405                 
9406                 this.tpl.append(this.el, d);
9407             }
9408         }
9409         this.updateIndexes(index);
9410     },
9411
9412     onRemove : function(ds, record, index){
9413         Roo.log('onRemove');
9414         this.clearSelections();
9415         var el = this.dataName  ?
9416             this.el.child('.roo-tpl-' + this.dataName) :
9417             this.el; 
9418         
9419         el.dom.removeChild(this.nodes[index]);
9420         this.updateIndexes(index);
9421     },
9422
9423     /**
9424      * Refresh an individual node.
9425      * @param {Number} index
9426      */
9427     refreshNode : function(index){
9428         this.onUpdate(this.store, this.store.getAt(index));
9429     },
9430
9431     updateIndexes : function(startIndex, endIndex){
9432         var ns = this.nodes;
9433         startIndex = startIndex || 0;
9434         endIndex = endIndex || ns.length - 1;
9435         for(var i = startIndex; i <= endIndex; i++){
9436             ns[i].nodeIndex = i;
9437         }
9438     },
9439
9440     /**
9441      * Changes the data store this view uses and refresh the view.
9442      * @param {Store} store
9443      */
9444     setStore : function(store, initial){
9445         if(!initial && this.store){
9446             this.store.un("datachanged", this.refresh);
9447             this.store.un("add", this.onAdd);
9448             this.store.un("remove", this.onRemove);
9449             this.store.un("update", this.onUpdate);
9450             this.store.un("clear", this.refresh);
9451             this.store.un("beforeload", this.onBeforeLoad);
9452             this.store.un("load", this.onLoad);
9453             this.store.un("loadexception", this.onLoad);
9454         }
9455         if(store){
9456           
9457             store.on("datachanged", this.refresh, this);
9458             store.on("add", this.onAdd, this);
9459             store.on("remove", this.onRemove, this);
9460             store.on("update", this.onUpdate, this);
9461             store.on("clear", this.refresh, this);
9462             store.on("beforeload", this.onBeforeLoad, this);
9463             store.on("load", this.onLoad, this);
9464             store.on("loadexception", this.onLoad, this);
9465         }
9466         
9467         if(store){
9468             this.refresh();
9469         }
9470     },
9471     /**
9472      * onbeforeLoad - masks the loading area.
9473      *
9474      */
9475     onBeforeLoad : function(store,opts)
9476     {
9477          Roo.log('onBeforeLoad');   
9478         if (!opts.add) {
9479             this.el.update("");
9480         }
9481         this.el.mask(this.mask ? this.mask : "Loading" ); 
9482     },
9483     onLoad : function ()
9484     {
9485         this.el.unmask();
9486     },
9487     
9488
9489     /**
9490      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9491      * @param {HTMLElement} node
9492      * @return {HTMLElement} The template node
9493      */
9494     findItemFromChild : function(node){
9495         var el = this.dataName  ?
9496             this.el.child('.roo-tpl-' + this.dataName,true) :
9497             this.el.dom; 
9498         
9499         if(!node || node.parentNode == el){
9500                     return node;
9501             }
9502             var p = node.parentNode;
9503             while(p && p != el){
9504             if(p.parentNode == el){
9505                 return p;
9506             }
9507             p = p.parentNode;
9508         }
9509             return null;
9510     },
9511
9512     /** @ignore */
9513     onClick : function(e){
9514         var item = this.findItemFromChild(e.getTarget());
9515         if(item){
9516             var index = this.indexOf(item);
9517             if(this.onItemClick(item, index, e) !== false){
9518                 this.fireEvent("click", this, index, item, e);
9519             }
9520         }else{
9521             this.clearSelections();
9522         }
9523     },
9524
9525     /** @ignore */
9526     onContextMenu : function(e){
9527         var item = this.findItemFromChild(e.getTarget());
9528         if(item){
9529             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9530         }
9531     },
9532
9533     /** @ignore */
9534     onDblClick : function(e){
9535         var item = this.findItemFromChild(e.getTarget());
9536         if(item){
9537             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9538         }
9539     },
9540
9541     onItemClick : function(item, index, e)
9542     {
9543         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9544             return false;
9545         }
9546         if (this.toggleSelect) {
9547             var m = this.isSelected(item) ? 'unselect' : 'select';
9548             Roo.log(m);
9549             var _t = this;
9550             _t[m](item, true, false);
9551             return true;
9552         }
9553         if(this.multiSelect || this.singleSelect){
9554             if(this.multiSelect && e.shiftKey && this.lastSelection){
9555                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9556             }else{
9557                 this.select(item, this.multiSelect && e.ctrlKey);
9558                 this.lastSelection = item;
9559             }
9560             e.preventDefault();
9561         }
9562         return true;
9563     },
9564
9565     /**
9566      * Get the number of selected nodes.
9567      * @return {Number}
9568      */
9569     getSelectionCount : function(){
9570         return this.selections.length;
9571     },
9572
9573     /**
9574      * Get the currently selected nodes.
9575      * @return {Array} An array of HTMLElements
9576      */
9577     getSelectedNodes : function(){
9578         return this.selections;
9579     },
9580
9581     /**
9582      * Get the indexes of the selected nodes.
9583      * @return {Array}
9584      */
9585     getSelectedIndexes : function(){
9586         var indexes = [], s = this.selections;
9587         for(var i = 0, len = s.length; i < len; i++){
9588             indexes.push(s[i].nodeIndex);
9589         }
9590         return indexes;
9591     },
9592
9593     /**
9594      * Clear all selections
9595      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9596      */
9597     clearSelections : function(suppressEvent){
9598         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9599             this.cmp.elements = this.selections;
9600             this.cmp.removeClass(this.selectedClass);
9601             this.selections = [];
9602             if(!suppressEvent){
9603                 this.fireEvent("selectionchange", this, this.selections);
9604             }
9605         }
9606     },
9607
9608     /**
9609      * Returns true if the passed node is selected
9610      * @param {HTMLElement/Number} node The node or node index
9611      * @return {Boolean}
9612      */
9613     isSelected : function(node){
9614         var s = this.selections;
9615         if(s.length < 1){
9616             return false;
9617         }
9618         node = this.getNode(node);
9619         return s.indexOf(node) !== -1;
9620     },
9621
9622     /**
9623      * Selects nodes.
9624      * @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
9625      * @param {Boolean} keepExisting (optional) true to keep existing selections
9626      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9627      */
9628     select : function(nodeInfo, keepExisting, suppressEvent){
9629         if(nodeInfo instanceof Array){
9630             if(!keepExisting){
9631                 this.clearSelections(true);
9632             }
9633             for(var i = 0, len = nodeInfo.length; i < len; i++){
9634                 this.select(nodeInfo[i], true, true);
9635             }
9636             return;
9637         } 
9638         var node = this.getNode(nodeInfo);
9639         if(!node || this.isSelected(node)){
9640             return; // already selected.
9641         }
9642         if(!keepExisting){
9643             this.clearSelections(true);
9644         }
9645         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9646             Roo.fly(node).addClass(this.selectedClass);
9647             this.selections.push(node);
9648             if(!suppressEvent){
9649                 this.fireEvent("selectionchange", this, this.selections);
9650             }
9651         }
9652         
9653         
9654     },
9655       /**
9656      * Unselects nodes.
9657      * @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
9658      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9659      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9660      */
9661     unselect : function(nodeInfo, keepExisting, suppressEvent)
9662     {
9663         if(nodeInfo instanceof Array){
9664             Roo.each(this.selections, function(s) {
9665                 this.unselect(s, nodeInfo);
9666             }, this);
9667             return;
9668         }
9669         var node = this.getNode(nodeInfo);
9670         if(!node || !this.isSelected(node)){
9671             Roo.log("not selected");
9672             return; // not selected.
9673         }
9674         // fireevent???
9675         var ns = [];
9676         Roo.each(this.selections, function(s) {
9677             if (s == node ) {
9678                 Roo.fly(node).removeClass(this.selectedClass);
9679
9680                 return;
9681             }
9682             ns.push(s);
9683         },this);
9684         
9685         this.selections= ns;
9686         this.fireEvent("selectionchange", this, this.selections);
9687     },
9688
9689     /**
9690      * Gets a template node.
9691      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9692      * @return {HTMLElement} The node or null if it wasn't found
9693      */
9694     getNode : function(nodeInfo){
9695         if(typeof nodeInfo == "string"){
9696             return document.getElementById(nodeInfo);
9697         }else if(typeof nodeInfo == "number"){
9698             return this.nodes[nodeInfo];
9699         }
9700         return nodeInfo;
9701     },
9702
9703     /**
9704      * Gets a range template nodes.
9705      * @param {Number} startIndex
9706      * @param {Number} endIndex
9707      * @return {Array} An array of nodes
9708      */
9709     getNodes : function(start, end){
9710         var ns = this.nodes;
9711         start = start || 0;
9712         end = typeof end == "undefined" ? ns.length - 1 : end;
9713         var nodes = [];
9714         if(start <= end){
9715             for(var i = start; i <= end; i++){
9716                 nodes.push(ns[i]);
9717             }
9718         } else{
9719             for(var i = start; i >= end; i--){
9720                 nodes.push(ns[i]);
9721             }
9722         }
9723         return nodes;
9724     },
9725
9726     /**
9727      * Finds the index of the passed node
9728      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9729      * @return {Number} The index of the node or -1
9730      */
9731     indexOf : function(node){
9732         node = this.getNode(node);
9733         if(typeof node.nodeIndex == "number"){
9734             return node.nodeIndex;
9735         }
9736         var ns = this.nodes;
9737         for(var i = 0, len = ns.length; i < len; i++){
9738             if(ns[i] == node){
9739                 return i;
9740             }
9741         }
9742         return -1;
9743     }
9744 });
9745 /*
9746  * - LGPL
9747  *
9748  * based on jquery fullcalendar
9749  * 
9750  */
9751
9752 Roo.bootstrap = Roo.bootstrap || {};
9753 /**
9754  * @class Roo.bootstrap.Calendar
9755  * @extends Roo.bootstrap.Component
9756  * Bootstrap Calendar class
9757  * @cfg {Boolean} loadMask (true|false) default false
9758     
9759  * @constructor
9760  * Create a new Container
9761  * @param {Object} config The config object
9762  */
9763
9764
9765
9766 Roo.bootstrap.Calendar = function(config){
9767     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9768      this.addEvents({
9769         /**
9770              * @event select
9771              * Fires when a date is selected
9772              * @param {DatePicker} this
9773              * @param {Date} date The selected date
9774              */
9775         'select': true,
9776         /**
9777              * @event monthchange
9778              * Fires when the displayed month changes 
9779              * @param {DatePicker} this
9780              * @param {Date} date The selected month
9781              */
9782         'monthchange': true,
9783         /**
9784              * @event evententer
9785              * Fires when mouse over an event
9786              * @param {Calendar} this
9787              * @param {event} Event
9788              */
9789         'evententer': true,
9790         /**
9791              * @event eventleave
9792              * Fires when the mouse leaves an
9793              * @param {Calendar} this
9794              * @param {event}
9795              */
9796         'eventleave': true,
9797         /**
9798              * @event eventclick
9799              * Fires when the mouse click an
9800              * @param {Calendar} this
9801              * @param {event}
9802              */
9803         'eventclick': true
9804         
9805     });
9806
9807 };
9808
9809 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9810     
9811      /**
9812      * @cfg {Number} startDay
9813      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9814      */
9815     startDay : 0,
9816     
9817     loadMask : false,
9818       
9819     getAutoCreate : function(){
9820         
9821         
9822         var fc_button = function(name, corner, style, content ) {
9823             return Roo.apply({},{
9824                 tag : 'span',
9825                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9826                          (corner.length ?
9827                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9828                             ''
9829                         ),
9830                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9831                 unselectable: 'on'
9832             });
9833         };
9834         
9835         var header = {
9836             tag : 'table',
9837             cls : 'fc-header',
9838             style : 'width:100%',
9839             cn : [
9840                 {
9841                     tag: 'tr',
9842                     cn : [
9843                         {
9844                             tag : 'td',
9845                             cls : 'fc-header-left',
9846                             cn : [
9847                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9848                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9849                                 { tag: 'span', cls: 'fc-header-space' },
9850                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9851                                 
9852                                 
9853                             ]
9854                         },
9855                         
9856                         {
9857                             tag : 'td',
9858                             cls : 'fc-header-center',
9859                             cn : [
9860                                 {
9861                                     tag: 'span',
9862                                     cls: 'fc-header-title',
9863                                     cn : {
9864                                         tag: 'H2',
9865                                         html : 'month / year'
9866                                     }
9867                                 }
9868                                 
9869                             ]
9870                         },
9871                         {
9872                             tag : 'td',
9873                             cls : 'fc-header-right',
9874                             cn : [
9875                           /*      fc_button('month', 'left', '', 'month' ),
9876                                 fc_button('week', '', '', 'week' ),
9877                                 fc_button('day', 'right', '', 'day' )
9878                             */    
9879                                 
9880                             ]
9881                         }
9882                         
9883                     ]
9884                 }
9885             ]
9886         };
9887         
9888        
9889         var cal_heads = function() {
9890             var ret = [];
9891             // fixme - handle this.
9892             
9893             for (var i =0; i < Date.dayNames.length; i++) {
9894                 var d = Date.dayNames[i];
9895                 ret.push({
9896                     tag: 'th',
9897                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9898                     html : d.substring(0,3)
9899                 });
9900                 
9901             }
9902             ret[0].cls += ' fc-first';
9903             ret[6].cls += ' fc-last';
9904             return ret;
9905         };
9906         var cal_cell = function(n) {
9907             return  {
9908                 tag: 'td',
9909                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9910                 cn : [
9911                     {
9912                         cn : [
9913                             {
9914                                 cls: 'fc-day-number',
9915                                 html: 'D'
9916                             },
9917                             {
9918                                 cls: 'fc-day-content',
9919                              
9920                                 cn : [
9921                                      {
9922                                         style: 'position: relative;' // height: 17px;
9923                                     }
9924                                 ]
9925                             }
9926                             
9927                             
9928                         ]
9929                     }
9930                 ]
9931                 
9932             }
9933         };
9934         var cal_rows = function() {
9935             
9936             var ret = []
9937             for (var r = 0; r < 6; r++) {
9938                 var row= {
9939                     tag : 'tr',
9940                     cls : 'fc-week',
9941                     cn : []
9942                 };
9943                 
9944                 for (var i =0; i < Date.dayNames.length; i++) {
9945                     var d = Date.dayNames[i];
9946                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9947
9948                 }
9949                 row.cn[0].cls+=' fc-first';
9950                 row.cn[0].cn[0].style = 'min-height:90px';
9951                 row.cn[6].cls+=' fc-last';
9952                 ret.push(row);
9953                 
9954             }
9955             ret[0].cls += ' fc-first';
9956             ret[4].cls += ' fc-prev-last';
9957             ret[5].cls += ' fc-last';
9958             return ret;
9959             
9960         };
9961         
9962         var cal_table = {
9963             tag: 'table',
9964             cls: 'fc-border-separate',
9965             style : 'width:100%',
9966             cellspacing  : 0,
9967             cn : [
9968                 { 
9969                     tag: 'thead',
9970                     cn : [
9971                         { 
9972                             tag: 'tr',
9973                             cls : 'fc-first fc-last',
9974                             cn : cal_heads()
9975                         }
9976                     ]
9977                 },
9978                 { 
9979                     tag: 'tbody',
9980                     cn : cal_rows()
9981                 }
9982                   
9983             ]
9984         };
9985          
9986          var cfg = {
9987             cls : 'fc fc-ltr',
9988             cn : [
9989                 header,
9990                 {
9991                     cls : 'fc-content',
9992                     style : "position: relative;",
9993                     cn : [
9994                         {
9995                             cls : 'fc-view fc-view-month fc-grid',
9996                             style : 'position: relative',
9997                             unselectable : 'on',
9998                             cn : [
9999                                 {
10000                                     cls : 'fc-event-container',
10001                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10002                                 },
10003                                 cal_table
10004                             ]
10005                         }
10006                     ]
10007     
10008                 }
10009            ] 
10010             
10011         };
10012         
10013          
10014         
10015         return cfg;
10016     },
10017     
10018     
10019     initEvents : function()
10020     {
10021         if(!this.store){
10022             throw "can not find store for calendar";
10023         }
10024         
10025         var mark = {
10026             tag: "div",
10027             cls:"x-dlg-mask",
10028             style: "text-align:center",
10029             cn: [
10030                 {
10031                     tag: "div",
10032                     style: "background-color:white;width:50%;margin:250 auto",
10033                     cn: [
10034                         {
10035                             tag: "img",
10036                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10037                         },
10038                         {
10039                             tag: "span",
10040                             html: "Loading"
10041                         }
10042                         
10043                     ]
10044                 }
10045             ]
10046         }
10047         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10048         
10049         var size = this.el.select('.fc-content', true).first().getSize();
10050         this.maskEl.setSize(size.width, size.height);
10051         this.maskEl.enableDisplayMode("block");
10052         if(!this.loadMask){
10053             this.maskEl.hide();
10054         }
10055         
10056         this.store = Roo.factory(this.store, Roo.data);
10057         this.store.on('load', this.onLoad, this);
10058         this.store.on('beforeload', this.onBeforeLoad, this);
10059         
10060         this.resize();
10061         
10062         this.cells = this.el.select('.fc-day',true);
10063         //Roo.log(this.cells);
10064         this.textNodes = this.el.query('.fc-day-number');
10065         this.cells.addClassOnOver('fc-state-hover');
10066         
10067         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10068         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10069         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10070         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10071         
10072         this.on('monthchange', this.onMonthChange, this);
10073         
10074         this.update(new Date().clearTime());
10075     },
10076     
10077     resize : function() {
10078         var sz  = this.el.getSize();
10079         
10080         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10081         this.el.select('.fc-day-content div',true).setHeight(34);
10082     },
10083     
10084     
10085     // private
10086     showPrevMonth : function(e){
10087         this.update(this.activeDate.add("mo", -1));
10088     },
10089     showToday : function(e){
10090         this.update(new Date().clearTime());
10091     },
10092     // private
10093     showNextMonth : function(e){
10094         this.update(this.activeDate.add("mo", 1));
10095     },
10096
10097     // private
10098     showPrevYear : function(){
10099         this.update(this.activeDate.add("y", -1));
10100     },
10101
10102     // private
10103     showNextYear : function(){
10104         this.update(this.activeDate.add("y", 1));
10105     },
10106
10107     
10108    // private
10109     update : function(date)
10110     {
10111         var vd = this.activeDate;
10112         this.activeDate = date;
10113 //        if(vd && this.el){
10114 //            var t = date.getTime();
10115 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10116 //                Roo.log('using add remove');
10117 //                
10118 //                this.fireEvent('monthchange', this, date);
10119 //                
10120 //                this.cells.removeClass("fc-state-highlight");
10121 //                this.cells.each(function(c){
10122 //                   if(c.dateValue == t){
10123 //                       c.addClass("fc-state-highlight");
10124 //                       setTimeout(function(){
10125 //                            try{c.dom.firstChild.focus();}catch(e){}
10126 //                       }, 50);
10127 //                       return false;
10128 //                   }
10129 //                   return true;
10130 //                });
10131 //                return;
10132 //            }
10133 //        }
10134         
10135         var days = date.getDaysInMonth();
10136         
10137         var firstOfMonth = date.getFirstDateOfMonth();
10138         var startingPos = firstOfMonth.getDay()-this.startDay;
10139         
10140         if(startingPos < this.startDay){
10141             startingPos += 7;
10142         }
10143         
10144         var pm = date.add(Date.MONTH, -1);
10145         var prevStart = pm.getDaysInMonth()-startingPos;
10146 //        
10147         this.cells = this.el.select('.fc-day',true);
10148         this.textNodes = this.el.query('.fc-day-number');
10149         this.cells.addClassOnOver('fc-state-hover');
10150         
10151         var cells = this.cells.elements;
10152         var textEls = this.textNodes;
10153         
10154         Roo.each(cells, function(cell){
10155             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10156         });
10157         
10158         days += startingPos;
10159
10160         // convert everything to numbers so it's fast
10161         var day = 86400000;
10162         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10163         //Roo.log(d);
10164         //Roo.log(pm);
10165         //Roo.log(prevStart);
10166         
10167         var today = new Date().clearTime().getTime();
10168         var sel = date.clearTime().getTime();
10169         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10170         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10171         var ddMatch = this.disabledDatesRE;
10172         var ddText = this.disabledDatesText;
10173         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10174         var ddaysText = this.disabledDaysText;
10175         var format = this.format;
10176         
10177         var setCellClass = function(cal, cell){
10178             
10179             //Roo.log('set Cell Class');
10180             cell.title = "";
10181             var t = d.getTime();
10182             
10183             //Roo.log(d);
10184             
10185             cell.dateValue = t;
10186             if(t == today){
10187                 cell.className += " fc-today";
10188                 cell.className += " fc-state-highlight";
10189                 cell.title = cal.todayText;
10190             }
10191             if(t == sel){
10192                 // disable highlight in other month..
10193                 //cell.className += " fc-state-highlight";
10194                 
10195             }
10196             // disabling
10197             if(t < min) {
10198                 cell.className = " fc-state-disabled";
10199                 cell.title = cal.minText;
10200                 return;
10201             }
10202             if(t > max) {
10203                 cell.className = " fc-state-disabled";
10204                 cell.title = cal.maxText;
10205                 return;
10206             }
10207             if(ddays){
10208                 if(ddays.indexOf(d.getDay()) != -1){
10209                     cell.title = ddaysText;
10210                     cell.className = " fc-state-disabled";
10211                 }
10212             }
10213             if(ddMatch && format){
10214                 var fvalue = d.dateFormat(format);
10215                 if(ddMatch.test(fvalue)){
10216                     cell.title = ddText.replace("%0", fvalue);
10217                     cell.className = " fc-state-disabled";
10218                 }
10219             }
10220             
10221             if (!cell.initialClassName) {
10222                 cell.initialClassName = cell.dom.className;
10223             }
10224             
10225             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10226         };
10227
10228         var i = 0;
10229         
10230         for(; i < startingPos; i++) {
10231             textEls[i].innerHTML = (++prevStart);
10232             d.setDate(d.getDate()+1);
10233             
10234             cells[i].className = "fc-past fc-other-month";
10235             setCellClass(this, cells[i]);
10236         }
10237         
10238         var intDay = 0;
10239         
10240         for(; i < days; i++){
10241             intDay = i - startingPos + 1;
10242             textEls[i].innerHTML = (intDay);
10243             d.setDate(d.getDate()+1);
10244             
10245             cells[i].className = ''; // "x-date-active";
10246             setCellClass(this, cells[i]);
10247         }
10248         var extraDays = 0;
10249         
10250         for(; i < 42; i++) {
10251             textEls[i].innerHTML = (++extraDays);
10252             d.setDate(d.getDate()+1);
10253             
10254             cells[i].className = "fc-future fc-other-month";
10255             setCellClass(this, cells[i]);
10256         }
10257         
10258         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10259         
10260         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10261         
10262         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10263         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10264         
10265         if(totalRows != 6){
10266             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10267             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10268         }
10269         
10270         this.fireEvent('monthchange', this, date);
10271         
10272         
10273         /*
10274         if(!this.internalRender){
10275             var main = this.el.dom.firstChild;
10276             var w = main.offsetWidth;
10277             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10278             Roo.fly(main).setWidth(w);
10279             this.internalRender = true;
10280             // opera does not respect the auto grow header center column
10281             // then, after it gets a width opera refuses to recalculate
10282             // without a second pass
10283             if(Roo.isOpera && !this.secondPass){
10284                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10285                 this.secondPass = true;
10286                 this.update.defer(10, this, [date]);
10287             }
10288         }
10289         */
10290         
10291     },
10292     
10293     findCell : function(dt) {
10294         dt = dt.clearTime().getTime();
10295         var ret = false;
10296         this.cells.each(function(c){
10297             //Roo.log("check " +c.dateValue + '?=' + dt);
10298             if(c.dateValue == dt){
10299                 ret = c;
10300                 return false;
10301             }
10302             return true;
10303         });
10304         
10305         return ret;
10306     },
10307     
10308     findCells : function(ev) {
10309         var s = ev.start.clone().clearTime().getTime();
10310        // Roo.log(s);
10311         var e= ev.end.clone().clearTime().getTime();
10312        // Roo.log(e);
10313         var ret = [];
10314         this.cells.each(function(c){
10315              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10316             
10317             if(c.dateValue > e){
10318                 return ;
10319             }
10320             if(c.dateValue < s){
10321                 return ;
10322             }
10323             ret.push(c);
10324         });
10325         
10326         return ret;    
10327     },
10328     
10329     findBestRow: function(cells)
10330     {
10331         var ret = 0;
10332         
10333         for (var i =0 ; i < cells.length;i++) {
10334             ret  = Math.max(cells[i].rows || 0,ret);
10335         }
10336         return ret;
10337         
10338     },
10339     
10340     
10341     addItem : function(ev)
10342     {
10343         // look for vertical location slot in
10344         var cells = this.findCells(ev);
10345         
10346         ev.row = this.findBestRow(cells);
10347         
10348         // work out the location.
10349         
10350         var crow = false;
10351         var rows = [];
10352         for(var i =0; i < cells.length; i++) {
10353             if (!crow) {
10354                 crow = {
10355                     start : cells[i],
10356                     end :  cells[i]
10357                 };
10358                 continue;
10359             }
10360             if (crow.start.getY() == cells[i].getY()) {
10361                 // on same row.
10362                 crow.end = cells[i];
10363                 continue;
10364             }
10365             // different row.
10366             rows.push(crow);
10367             crow = {
10368                 start: cells[i],
10369                 end : cells[i]
10370             };
10371             
10372         }
10373         
10374         rows.push(crow);
10375         ev.els = [];
10376         ev.rows = rows;
10377         ev.cells = cells;
10378         for (var i = 0; i < cells.length;i++) {
10379             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10380             
10381         }
10382         
10383         this.calevents.push(ev);
10384     },
10385     
10386     clearEvents: function() {
10387         
10388         if(!this.calevents){
10389             return;
10390         }
10391         
10392         Roo.each(this.cells.elements, function(c){
10393             c.rows = 0;
10394         });
10395         
10396         Roo.each(this.calevents, function(e) {
10397             Roo.each(e.els, function(el) {
10398                 el.un('mouseenter' ,this.onEventEnter, this);
10399                 el.un('mouseleave' ,this.onEventLeave, this);
10400                 el.remove();
10401             },this);
10402         },this);
10403         
10404     },
10405     
10406     renderEvents: function()
10407     {   
10408         // first make sure there is enough space..
10409         
10410         this.cells.each(function(c) {
10411         
10412             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10413         });
10414         
10415         for (var e = 0; e < this.calevents.length; e++) {
10416             var ev = this.calevents[e];
10417             var cells = ev.cells;
10418             var rows = ev.rows;
10419             
10420             for(var i =0; i < rows.length; i++) {
10421                 
10422                  
10423                 // how many rows should it span..
10424                 
10425                 var  cfg = {
10426                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10427                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10428                     
10429                     unselectable : "on",
10430                     cn : [
10431                         {
10432                             cls: 'fc-event-inner',
10433                             cn : [
10434 //                                {
10435 //                                  tag:'span',
10436 //                                  cls: 'fc-event-time',
10437 //                                  html : cells.length > 1 ? '' : ev.time
10438 //                                },
10439                                 {
10440                                   tag:'span',
10441                                   cls: 'fc-event-title',
10442                                   html : String.format('{0}', ev.title)
10443                                 }
10444                                 
10445                                 
10446                             ]
10447                         },
10448                         {
10449                             cls: 'ui-resizable-handle ui-resizable-e',
10450                             html : '&nbsp;&nbsp;&nbsp'
10451                         }
10452                         
10453                     ]
10454                 };
10455                 if (i == 0) {
10456                     cfg.cls += ' fc-event-start';
10457                 }
10458                 if ((i+1) == rows.length) {
10459                     cfg.cls += ' fc-event-end';
10460                 }
10461                 
10462                 var ctr = this.el.select('.fc-event-container',true).first();
10463                 var cg = ctr.createChild(cfg);
10464                 
10465                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10466                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10467                 cg.on('click', this.onEventClick, this, ev);
10468                 
10469                 ev.els.push(cg);
10470                 
10471                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10472                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10473                 //Roo.log(cg);
10474                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10475                 cg.setWidth(ebox.right - sbox.x -2);
10476             }
10477             
10478             
10479         }
10480         
10481     },
10482     
10483     onEventEnter: function (e, el,event,d) {
10484         this.fireEvent('evententer', this, el, event);
10485     },
10486     
10487     onEventLeave: function (e, el,event,d) {
10488         this.fireEvent('eventleave', this, el, event);
10489     },
10490     
10491     onEventClick: function (e, el,event,d) {
10492         this.fireEvent('eventclick', this, el, event);
10493     },
10494     
10495     onMonthChange: function () {
10496         this.store.load();
10497     },
10498     
10499     onLoad: function () 
10500     {   
10501         this.calevents = [];
10502         var cal = this;
10503         
10504         if(this.store.getCount() > 0){
10505             this.store.data.each(function(d){
10506                cal.addItem({
10507                     id : d.data.id,
10508                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10509                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10510                     time : d.data.start_time,
10511                     title : d.data.title,
10512                     description : d.data.description,
10513                     venue : d.data.venue
10514                 });
10515             });
10516         }
10517         
10518         this.renderEvents();
10519         
10520         if(this.loadMask){
10521             this.maskEl.hide();
10522         }
10523     },
10524     
10525     onBeforeLoad: function()
10526     {
10527         this.clearEvents();
10528         
10529         if(this.loadMask){
10530             this.maskEl.show();
10531         }
10532     }
10533 });
10534
10535  
10536  /*
10537  * - LGPL
10538  *
10539  * element
10540  * 
10541  */
10542
10543 /**
10544  * @class Roo.bootstrap.Popover
10545  * @extends Roo.bootstrap.Component
10546  * Bootstrap Popover class
10547  * @cfg {String} html contents of the popover   (or false to use children..)
10548  * @cfg {String} title of popover (or false to hide)
10549  * @cfg {String} placement how it is placed
10550  * @cfg {String} trigger click || hover (or false to trigger manually)
10551  * @cfg {String} over what (parent or false to trigger manually.)
10552  * 
10553  * @constructor
10554  * Create a new Popover
10555  * @param {Object} config The config object
10556  */
10557
10558 Roo.bootstrap.Popover = function(config){
10559     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10560 };
10561
10562 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10563     
10564     title: 'Fill in a title',
10565     html: false,
10566     
10567     placement : 'right',
10568     trigger : 'hover', // hover
10569     
10570     over: 'parent',
10571     
10572     can_build_overlaid : false,
10573     
10574     getChildContainer : function()
10575     {
10576         return this.el.select('.popover-content',true).first();
10577     },
10578     
10579     getAutoCreate : function(){
10580          Roo.log('make popover?');
10581         var cfg = {
10582            cls : 'popover roo-dynamic',
10583            style: 'display:block',
10584            cn : [
10585                 {
10586                     cls : 'arrow'
10587                 },
10588                 {
10589                     cls : 'popover-inner',
10590                     cn : [
10591                         {
10592                             tag: 'h3',
10593                             cls: 'popover-title',
10594                             html : this.title
10595                         },
10596                         {
10597                             cls : 'popover-content',
10598                             html : this.html
10599                         }
10600                     ]
10601                     
10602                 }
10603            ]
10604         };
10605         
10606         return cfg;
10607     },
10608     setTitle: function(str)
10609     {
10610         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10611     },
10612     setContent: function(str)
10613     {
10614         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10615     },
10616     // as it get's added to the bottom of the page.
10617     onRender : function(ct, position)
10618     {
10619         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10620         if(!this.el){
10621             var cfg = Roo.apply({},  this.getAutoCreate());
10622             cfg.id = Roo.id();
10623             
10624             if (this.cls) {
10625                 cfg.cls += ' ' + this.cls;
10626             }
10627             if (this.style) {
10628                 cfg.style = this.style;
10629             }
10630             Roo.log("adding to ")
10631             this.el = Roo.get(document.body).createChild(cfg, position);
10632             Roo.log(this.el);
10633         }
10634         this.initEvents();
10635     },
10636     
10637     initEvents : function()
10638     {
10639         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10640         this.el.enableDisplayMode('block');
10641         this.el.hide();
10642         if (this.over === false) {
10643             return; 
10644         }
10645         if (this.triggers === false) {
10646             return;
10647         }
10648         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10649         var triggers = this.trigger ? this.trigger.split(' ') : [];
10650         Roo.each(triggers, function(trigger) {
10651         
10652             if (trigger == 'click') {
10653                 on_el.on('click', this.toggle, this);
10654             } else if (trigger != 'manual') {
10655                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10656                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10657       
10658                 on_el.on(eventIn  ,this.enter, this);
10659                 on_el.on(eventOut, this.leave, this);
10660             }
10661         }, this);
10662         
10663     },
10664     
10665     
10666     // private
10667     timeout : null,
10668     hoverState : null,
10669     
10670     toggle : function () {
10671         this.hoverState == 'in' ? this.leave() : this.enter();
10672     },
10673     
10674     enter : function () {
10675        
10676     
10677         clearTimeout(this.timeout);
10678     
10679         this.hoverState = 'in'
10680     
10681         if (!this.delay || !this.delay.show) {
10682             this.show();
10683             return 
10684         }
10685         var _t = this;
10686         this.timeout = setTimeout(function () {
10687             if (_t.hoverState == 'in') {
10688                 _t.show();
10689             }
10690         }, this.delay.show)
10691     },
10692     leave : function() {
10693         clearTimeout(this.timeout);
10694     
10695         this.hoverState = 'out'
10696     
10697         if (!this.delay || !this.delay.hide) {
10698             this.hide();
10699             return 
10700         }
10701         var _t = this;
10702         this.timeout = setTimeout(function () {
10703             if (_t.hoverState == 'out') {
10704                 _t.hide();
10705             }
10706         }, this.delay.hide)
10707     },
10708     
10709     show : function (on_el)
10710     {
10711         if (!on_el) {
10712             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10713         }
10714         // set content.
10715         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10716         if (this.html !== false) {
10717             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10718         }
10719         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10720         if (!this.title.length) {
10721             this.el.select('.popover-title',true).hide();
10722         }
10723         
10724         var placement = typeof this.placement == 'function' ?
10725             this.placement.call(this, this.el, on_el) :
10726             this.placement;
10727             
10728         var autoToken = /\s?auto?\s?/i;
10729         var autoPlace = autoToken.test(placement);
10730         if (autoPlace) {
10731             placement = placement.replace(autoToken, '') || 'top';
10732         }
10733         
10734         //this.el.detach()
10735         //this.el.setXY([0,0]);
10736         this.el.show();
10737         this.el.dom.style.display='block';
10738         this.el.addClass(placement);
10739         
10740         //this.el.appendTo(on_el);
10741         
10742         var p = this.getPosition();
10743         var box = this.el.getBox();
10744         
10745         if (autoPlace) {
10746             // fixme..
10747         }
10748         var align = Roo.bootstrap.Popover.alignment[placement]
10749         this.el.alignTo(on_el, align[0],align[1]);
10750         //var arrow = this.el.select('.arrow',true).first();
10751         //arrow.set(align[2], 
10752         
10753         this.el.addClass('in');
10754         this.hoverState = null;
10755         
10756         if (this.el.hasClass('fade')) {
10757             // fade it?
10758         }
10759         
10760     },
10761     hide : function()
10762     {
10763         this.el.setXY([0,0]);
10764         this.el.removeClass('in');
10765         this.el.hide();
10766         
10767     }
10768     
10769 });
10770
10771 Roo.bootstrap.Popover.alignment = {
10772     'left' : ['r-l', [-10,0], 'right'],
10773     'right' : ['l-r', [10,0], 'left'],
10774     'bottom' : ['t-b', [0,10], 'top'],
10775     'top' : [ 'b-t', [0,-10], 'bottom']
10776 };
10777
10778  /*
10779  * - LGPL
10780  *
10781  * Progress
10782  * 
10783  */
10784
10785 /**
10786  * @class Roo.bootstrap.Progress
10787  * @extends Roo.bootstrap.Component
10788  * Bootstrap Progress class
10789  * @cfg {Boolean} striped striped of the progress bar
10790  * @cfg {Boolean} active animated of the progress bar
10791  * 
10792  * 
10793  * @constructor
10794  * Create a new Progress
10795  * @param {Object} config The config object
10796  */
10797
10798 Roo.bootstrap.Progress = function(config){
10799     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10800 };
10801
10802 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10803     
10804     striped : false,
10805     active: false,
10806     
10807     getAutoCreate : function(){
10808         var cfg = {
10809             tag: 'div',
10810             cls: 'progress'
10811         };
10812         
10813         
10814         if(this.striped){
10815             cfg.cls += ' progress-striped';
10816         }
10817       
10818         if(this.active){
10819             cfg.cls += ' active';
10820         }
10821         
10822         
10823         return cfg;
10824     }
10825    
10826 });
10827
10828  
10829
10830  /*
10831  * - LGPL
10832  *
10833  * ProgressBar
10834  * 
10835  */
10836
10837 /**
10838  * @class Roo.bootstrap.ProgressBar
10839  * @extends Roo.bootstrap.Component
10840  * Bootstrap ProgressBar class
10841  * @cfg {Number} aria_valuenow aria-value now
10842  * @cfg {Number} aria_valuemin aria-value min
10843  * @cfg {Number} aria_valuemax aria-value max
10844  * @cfg {String} label label for the progress bar
10845  * @cfg {String} panel (success | info | warning | danger )
10846  * @cfg {String} role role of the progress bar
10847  * @cfg {String} sr_only text
10848  * 
10849  * 
10850  * @constructor
10851  * Create a new ProgressBar
10852  * @param {Object} config The config object
10853  */
10854
10855 Roo.bootstrap.ProgressBar = function(config){
10856     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10857 };
10858
10859 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10860     
10861     aria_valuenow : 0,
10862     aria_valuemin : 0,
10863     aria_valuemax : 100,
10864     label : false,
10865     panel : false,
10866     role : false,
10867     sr_only: false,
10868     
10869     getAutoCreate : function()
10870     {
10871         
10872         var cfg = {
10873             tag: 'div',
10874             cls: 'progress-bar',
10875             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10876         };
10877         
10878         if(this.sr_only){
10879             cfg.cn = {
10880                 tag: 'span',
10881                 cls: 'sr-only',
10882                 html: this.sr_only
10883             }
10884         }
10885         
10886         if(this.role){
10887             cfg.role = this.role;
10888         }
10889         
10890         if(this.aria_valuenow){
10891             cfg['aria-valuenow'] = this.aria_valuenow;
10892         }
10893         
10894         if(this.aria_valuemin){
10895             cfg['aria-valuemin'] = this.aria_valuemin;
10896         }
10897         
10898         if(this.aria_valuemax){
10899             cfg['aria-valuemax'] = this.aria_valuemax;
10900         }
10901         
10902         if(this.label && !this.sr_only){
10903             cfg.html = this.label;
10904         }
10905         
10906         if(this.panel){
10907             cfg.cls += ' progress-bar-' + this.panel;
10908         }
10909         
10910         return cfg;
10911     },
10912     
10913     update : function(aria_valuenow)
10914     {
10915         this.aria_valuenow = aria_valuenow;
10916         
10917         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10918     }
10919    
10920 });
10921
10922  
10923
10924  /*
10925  * - LGPL
10926  *
10927  * TabPanel
10928  * 
10929  */
10930
10931 /**
10932  * @class Roo.bootstrap.TabPanel
10933  * @extends Roo.bootstrap.Component
10934  * Bootstrap TabPanel class
10935  * @cfg {Boolean} active panel active
10936  * @cfg {String} html panel content
10937  * @cfg {String} tabId tab relate id
10938  * 
10939  * 
10940  * @constructor
10941  * Create a new TabPanel
10942  * @param {Object} config The config object
10943  */
10944
10945 Roo.bootstrap.TabPanel = function(config){
10946     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10947 };
10948
10949 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10950     
10951     active: false,
10952     html: false,
10953     tabId: false,
10954     
10955     getAutoCreate : function(){
10956         var cfg = {
10957             tag: 'div',
10958             cls: 'tab-pane',
10959             html: this.html || ''
10960         };
10961         
10962         if(this.active){
10963             cfg.cls += ' active';
10964         }
10965         
10966         if(this.tabId){
10967             cfg.tabId = this.tabId;
10968         }
10969         
10970         return cfg;
10971     }
10972    
10973 });
10974
10975  
10976
10977  /*
10978  * - LGPL
10979  *
10980  * DateField
10981  * 
10982  */
10983
10984 /**
10985  * @class Roo.bootstrap.DateField
10986  * @extends Roo.bootstrap.Input
10987  * Bootstrap DateField class
10988  * @cfg {Number} weekStart default 0
10989  * @cfg {Number} weekStart default 0
10990  * @cfg {Number} viewMode default empty, (months|years)
10991  * @cfg {Number} minViewMode default empty, (months|years)
10992  * @cfg {Number} startDate default -Infinity
10993  * @cfg {Number} endDate default Infinity
10994  * @cfg {Boolean} todayHighlight default false
10995  * @cfg {Boolean} todayBtn default false
10996  * @cfg {Boolean} calendarWeeks default false
10997  * @cfg {Object} daysOfWeekDisabled default empty
10998  * 
10999  * @cfg {Boolean} keyboardNavigation default true
11000  * @cfg {String} language default en
11001  * 
11002  * @constructor
11003  * Create a new DateField
11004  * @param {Object} config The config object
11005  */
11006
11007 Roo.bootstrap.DateField = function(config){
11008     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11009      this.addEvents({
11010             /**
11011              * @event show
11012              * Fires when this field show.
11013              * @param {Roo.bootstrap.DateField} this
11014              * @param {Mixed} date The date value
11015              */
11016             show : true,
11017             /**
11018              * @event show
11019              * Fires when this field hide.
11020              * @param {Roo.bootstrap.DateField} this
11021              * @param {Mixed} date The date value
11022              */
11023             hide : true,
11024             /**
11025              * @event select
11026              * Fires when select a date.
11027              * @param {Roo.bootstrap.DateField} this
11028              * @param {Mixed} date The date value
11029              */
11030             select : true
11031         });
11032 };
11033
11034 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11035     
11036     /**
11037      * @cfg {String} format
11038      * The default date format string which can be overriden for localization support.  The format must be
11039      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11040      */
11041     format : "m/d/y",
11042     /**
11043      * @cfg {String} altFormats
11044      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11045      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11046      */
11047     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11048     
11049     weekStart : 0,
11050     
11051     viewMode : '',
11052     
11053     minViewMode : '',
11054     
11055     todayHighlight : false,
11056     
11057     todayBtn: false,
11058     
11059     language: 'en',
11060     
11061     keyboardNavigation: true,
11062     
11063     calendarWeeks: false,
11064     
11065     startDate: -Infinity,
11066     
11067     endDate: Infinity,
11068     
11069     daysOfWeekDisabled: [],
11070     
11071     _events: [],
11072     
11073     UTCDate: function()
11074     {
11075         return new Date(Date.UTC.apply(Date, arguments));
11076     },
11077     
11078     UTCToday: function()
11079     {
11080         var today = new Date();
11081         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11082     },
11083     
11084     getDate: function() {
11085             var d = this.getUTCDate();
11086             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11087     },
11088     
11089     getUTCDate: function() {
11090             return this.date;
11091     },
11092     
11093     setDate: function(d) {
11094             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11095     },
11096     
11097     setUTCDate: function(d) {
11098             this.date = d;
11099             this.setValue(this.formatDate(this.date));
11100     },
11101         
11102     onRender: function(ct, position)
11103     {
11104         
11105         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11106         
11107         this.language = this.language || 'en';
11108         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11109         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11110         
11111         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11112         this.format = this.format || 'm/d/y';
11113         this.isInline = false;
11114         this.isInput = true;
11115         this.component = this.el.select('.add-on', true).first() || false;
11116         this.component = (this.component && this.component.length === 0) ? false : this.component;
11117         this.hasInput = this.component && this.inputEL().length;
11118         
11119         if (typeof(this.minViewMode === 'string')) {
11120             switch (this.minViewMode) {
11121                 case 'months':
11122                     this.minViewMode = 1;
11123                     break;
11124                 case 'years':
11125                     this.minViewMode = 2;
11126                     break;
11127                 default:
11128                     this.minViewMode = 0;
11129                     break;
11130             }
11131         }
11132         
11133         if (typeof(this.viewMode === 'string')) {
11134             switch (this.viewMode) {
11135                 case 'months':
11136                     this.viewMode = 1;
11137                     break;
11138                 case 'years':
11139                     this.viewMode = 2;
11140                     break;
11141                 default:
11142                     this.viewMode = 0;
11143                     break;
11144             }
11145         }
11146                 
11147         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11148         
11149         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11150         
11151         this.picker().on('mousedown', this.onMousedown, this);
11152         this.picker().on('click', this.onClick, this);
11153         
11154         this.picker().addClass('datepicker-dropdown');
11155         
11156         this.startViewMode = this.viewMode;
11157         
11158         
11159         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11160             if(!this.calendarWeeks){
11161                 v.remove();
11162                 return;
11163             };
11164             
11165             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11166             v.attr('colspan', function(i, val){
11167                 return parseInt(val) + 1;
11168             });
11169         })
11170                         
11171         
11172         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11173         
11174         this.setStartDate(this.startDate);
11175         this.setEndDate(this.endDate);
11176         
11177         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11178         
11179         this.fillDow();
11180         this.fillMonths();
11181         this.update();
11182         this.showMode();
11183         
11184         if(this.isInline) {
11185             this.show();
11186         }
11187     },
11188     
11189     picker : function()
11190     {
11191         return this.el.select('.datepicker', true).first();
11192     },
11193     
11194     fillDow: function()
11195     {
11196         var dowCnt = this.weekStart;
11197         
11198         var dow = {
11199             tag: 'tr',
11200             cn: [
11201                 
11202             ]
11203         };
11204         
11205         if(this.calendarWeeks){
11206             dow.cn.push({
11207                 tag: 'th',
11208                 cls: 'cw',
11209                 html: '&nbsp;'
11210             })
11211         }
11212         
11213         while (dowCnt < this.weekStart + 7) {
11214             dow.cn.push({
11215                 tag: 'th',
11216                 cls: 'dow',
11217                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11218             });
11219         }
11220         
11221         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11222     },
11223     
11224     fillMonths: function()
11225     {    
11226         var i = 0
11227         var months = this.picker().select('>.datepicker-months td', true).first();
11228         
11229         months.dom.innerHTML = '';
11230         
11231         while (i < 12) {
11232             var month = {
11233                 tag: 'span',
11234                 cls: 'month',
11235                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11236             }
11237             
11238             months.createChild(month);
11239         }
11240         
11241     },
11242     
11243     update: function(){
11244         
11245         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11246         
11247         if (this.date < this.startDate) {
11248             this.viewDate = new Date(this.startDate);
11249         } else if (this.date > this.endDate) {
11250             this.viewDate = new Date(this.endDate);
11251         } else {
11252             this.viewDate = new Date(this.date);
11253         }
11254         
11255         this.fill();
11256     },
11257     
11258     fill: function() {
11259         var d = new Date(this.viewDate),
11260                 year = d.getUTCFullYear(),
11261                 month = d.getUTCMonth(),
11262                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11263                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11264                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11265                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11266                 currentDate = this.date && this.date.valueOf(),
11267                 today = this.UTCToday();
11268         
11269         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11270         
11271 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11272         
11273 //        this.picker.select('>tfoot th.today').
11274 //                                              .text(dates[this.language].today)
11275 //                                              .toggle(this.todayBtn !== false);
11276     
11277         this.updateNavArrows();
11278         this.fillMonths();
11279                                                 
11280         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11281         
11282         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11283          
11284         prevMonth.setUTCDate(day);
11285         
11286         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11287         
11288         var nextMonth = new Date(prevMonth);
11289         
11290         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11291         
11292         nextMonth = nextMonth.valueOf();
11293         
11294         var fillMonths = false;
11295         
11296         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11297         
11298         while(prevMonth.valueOf() < nextMonth) {
11299             var clsName = '';
11300             
11301             if (prevMonth.getUTCDay() === this.weekStart) {
11302                 if(fillMonths){
11303                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11304                 }
11305                     
11306                 fillMonths = {
11307                     tag: 'tr',
11308                     cn: []
11309                 };
11310                 
11311                 if(this.calendarWeeks){
11312                     // ISO 8601: First week contains first thursday.
11313                     // ISO also states week starts on Monday, but we can be more abstract here.
11314                     var
11315                     // Start of current week: based on weekstart/current date
11316                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11317                     // Thursday of this week
11318                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11319                     // First Thursday of year, year from thursday
11320                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11321                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11322                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11323                     
11324                     fillMonths.cn.push({
11325                         tag: 'td',
11326                         cls: 'cw',
11327                         html: calWeek
11328                     });
11329                 }
11330             }
11331             
11332             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11333                 clsName += ' old';
11334             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11335                 clsName += ' new';
11336             }
11337             if (this.todayHighlight &&
11338                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11339                 prevMonth.getUTCMonth() == today.getMonth() &&
11340                 prevMonth.getUTCDate() == today.getDate()) {
11341                 clsName += ' today';
11342             }
11343             
11344             if (currentDate && prevMonth.valueOf() === currentDate) {
11345                 clsName += ' active';
11346             }
11347             
11348             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11349                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11350                     clsName += ' disabled';
11351             }
11352             
11353             fillMonths.cn.push({
11354                 tag: 'td',
11355                 cls: 'day ' + clsName,
11356                 html: prevMonth.getDate()
11357             })
11358             
11359             prevMonth.setDate(prevMonth.getDate()+1);
11360         }
11361           
11362         var currentYear = this.date && this.date.getUTCFullYear();
11363         var currentMonth = this.date && this.date.getUTCMonth();
11364         
11365         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11366         
11367         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11368             v.removeClass('active');
11369             
11370             if(currentYear === year && k === currentMonth){
11371                 v.addClass('active');
11372             }
11373             
11374             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11375                 v.addClass('disabled');
11376             }
11377             
11378         });
11379         
11380         
11381         year = parseInt(year/10, 10) * 10;
11382         
11383         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11384         
11385         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11386         
11387         year -= 1;
11388         for (var i = -1; i < 11; i++) {
11389             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11390                 tag: 'span',
11391                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11392                 html: year
11393             })
11394             
11395             year += 1;
11396         }
11397     },
11398     
11399     showMode: function(dir) {
11400         if (dir) {
11401             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11402         }
11403         Roo.each(this.picker().select('>div',true).elements, function(v){
11404             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11405             v.hide();
11406         });
11407         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11408     },
11409     
11410     place: function()
11411     {
11412         if(this.isInline) return;
11413         
11414         this.picker().removeClass(['bottom', 'top']);
11415         
11416         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11417             /*
11418              * place to the top of element!
11419              *
11420              */
11421             
11422             this.picker().addClass('top');
11423             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11424             
11425             return;
11426         }
11427         
11428         this.picker().addClass('bottom');
11429         
11430         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11431     },
11432     
11433     parseDate : function(value){
11434         if(!value || value instanceof Date){
11435             return value;
11436         }
11437         var v = Date.parseDate(value, this.format);
11438         if (!v && this.useIso) {
11439             v = Date.parseDate(value, 'Y-m-d');
11440         }
11441         if(!v && this.altFormats){
11442             if(!this.altFormatsArray){
11443                 this.altFormatsArray = this.altFormats.split("|");
11444             }
11445             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11446                 v = Date.parseDate(value, this.altFormatsArray[i]);
11447             }
11448         }
11449         return v;
11450     },
11451     
11452     formatDate : function(date, fmt){
11453         return (!date || !(date instanceof Date)) ?
11454         date : date.dateFormat(fmt || this.format);
11455     },
11456     
11457     onFocus : function()
11458     {
11459         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11460         this.show();
11461     },
11462     
11463     onBlur : function()
11464     {
11465         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11466         this.hide();
11467     },
11468     
11469     show : function()
11470     {
11471         this.picker().show();
11472         this.update();
11473         this.place();
11474         
11475         this.fireEvent('show', this, this.date);
11476     },
11477     
11478     hide : function()
11479     {
11480         if(this.isInline) return;
11481         this.picker().hide();
11482         this.viewMode = this.startViewMode;
11483         this.showMode();
11484         
11485         this.fireEvent('hide', this, this.date);
11486         
11487     },
11488     
11489     onMousedown: function(e){
11490         e.stopPropagation();
11491         e.preventDefault();
11492     },
11493     
11494     keyup: function(e){
11495         Roo.bootstrap.DateField.superclass.keyup.call(this);
11496         this.update();
11497         
11498     },
11499
11500     setValue: function(v){
11501         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11502         
11503         this.fireEvent('select', this, this.date);
11504         
11505     },
11506     
11507     fireKey: function(e){
11508         if (!this.picker().isVisible()){
11509             if (e.keyCode == 27) // allow escape to hide and re-show picker
11510                 this.show();
11511             return;
11512         }
11513         var dateChanged = false,
11514         dir, day, month,
11515         newDate, newViewDate;
11516         switch(e.keyCode){
11517             case 27: // escape
11518                 this.hide();
11519                 e.preventDefault();
11520                 break;
11521             case 37: // left
11522             case 39: // right
11523                 if (!this.keyboardNavigation) break;
11524                 dir = e.keyCode == 37 ? -1 : 1;
11525                 
11526                 if (e.ctrlKey){
11527                     newDate = this.moveYear(this.date, dir);
11528                     newViewDate = this.moveYear(this.viewDate, dir);
11529                 } else if (e.shiftKey){
11530                     newDate = this.moveMonth(this.date, dir);
11531                     newViewDate = this.moveMonth(this.viewDate, dir);
11532                 } else {
11533                     newDate = new Date(this.date);
11534                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11535                     newViewDate = new Date(this.viewDate);
11536                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11537                 }
11538                 if (this.dateWithinRange(newDate)){
11539                     this.date = newDate;
11540                     this.viewDate = newViewDate;
11541                     this.setValue(this.formatDate(this.date));
11542                     this.update();
11543                     e.preventDefault();
11544                     dateChanged = true;
11545                 }
11546                 break;
11547             case 38: // up
11548             case 40: // down
11549                 if (!this.keyboardNavigation) break;
11550                 dir = e.keyCode == 38 ? -1 : 1;
11551                 if (e.ctrlKey){
11552                     newDate = this.moveYear(this.date, dir);
11553                     newViewDate = this.moveYear(this.viewDate, dir);
11554                 } else if (e.shiftKey){
11555                     newDate = this.moveMonth(this.date, dir);
11556                     newViewDate = this.moveMonth(this.viewDate, dir);
11557                 } else {
11558                     newDate = new Date(this.date);
11559                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11560                     newViewDate = new Date(this.viewDate);
11561                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11562                 }
11563                 if (this.dateWithinRange(newDate)){
11564                     this.date = newDate;
11565                     this.viewDate = newViewDate;
11566                     this.setValue(this.formatDate(this.date));
11567                     this.update();
11568                     e.preventDefault();
11569                     dateChanged = true;
11570                 }
11571                 break;
11572             case 13: // enter
11573                 this.setValue(this.formatDate(this.date));
11574                 this.hide();
11575                 e.preventDefault();
11576                 break;
11577             case 9: // tab
11578                 this.setValue(this.formatDate(this.date));
11579                 this.hide();
11580                 break;
11581         }
11582     },
11583     
11584     
11585     onClick: function(e) {
11586         e.stopPropagation();
11587         e.preventDefault();
11588         
11589         var target = e.getTarget();
11590         
11591         if(target.nodeName.toLowerCase() === 'i'){
11592             target = Roo.get(target).dom.parentNode;
11593         }
11594         
11595         var nodeName = target.nodeName;
11596         var className = target.className;
11597         var html = target.innerHTML;
11598         
11599         switch(nodeName.toLowerCase()) {
11600             case 'th':
11601                 switch(className) {
11602                     case 'switch':
11603                         this.showMode(1);
11604                         break;
11605                     case 'prev':
11606                     case 'next':
11607                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11608                         switch(this.viewMode){
11609                                 case 0:
11610                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11611                                         break;
11612                                 case 1:
11613                                 case 2:
11614                                         this.viewDate = this.moveYear(this.viewDate, dir);
11615                                         break;
11616                         }
11617                         this.fill();
11618                         break;
11619                     case 'today':
11620                         var date = new Date();
11621                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11622                         this.fill()
11623                         this.setValue(this.formatDate(this.date));
11624                         this.hide();
11625                         break;
11626                 }
11627                 break;
11628             case 'span':
11629                 if (className.indexOf('disabled') === -1) {
11630                     this.viewDate.setUTCDate(1);
11631                     if (className.indexOf('month') !== -1) {
11632                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11633                     } else {
11634                         var year = parseInt(html, 10) || 0;
11635                         this.viewDate.setUTCFullYear(year);
11636                         
11637                     }
11638                     this.showMode(-1);
11639                     this.fill();
11640                 }
11641                 break;
11642                 
11643             case 'td':
11644                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11645                     var day = parseInt(html, 10) || 1;
11646                     var year = this.viewDate.getUTCFullYear(),
11647                         month = this.viewDate.getUTCMonth();
11648
11649                     if (className.indexOf('old') !== -1) {
11650                         if(month === 0 ){
11651                             month = 11;
11652                             year -= 1;
11653                         }else{
11654                             month -= 1;
11655                         }
11656                     } else if (className.indexOf('new') !== -1) {
11657                         if (month == 11) {
11658                             month = 0;
11659                             year += 1;
11660                         } else {
11661                             month += 1;
11662                         }
11663                     }
11664                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11665                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11666                     this.fill();
11667                     this.setValue(this.formatDate(this.date));
11668                     this.hide();
11669                 }
11670                 break;
11671         }
11672     },
11673     
11674     setStartDate: function(startDate){
11675         this.startDate = startDate || -Infinity;
11676         if (this.startDate !== -Infinity) {
11677             this.startDate = this.parseDate(this.startDate);
11678         }
11679         this.update();
11680         this.updateNavArrows();
11681     },
11682
11683     setEndDate: function(endDate){
11684         this.endDate = endDate || Infinity;
11685         if (this.endDate !== Infinity) {
11686             this.endDate = this.parseDate(this.endDate);
11687         }
11688         this.update();
11689         this.updateNavArrows();
11690     },
11691     
11692     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11693         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11694         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11695             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11696         }
11697         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11698             return parseInt(d, 10);
11699         });
11700         this.update();
11701         this.updateNavArrows();
11702     },
11703     
11704     updateNavArrows: function() {
11705         var d = new Date(this.viewDate),
11706         year = d.getUTCFullYear(),
11707         month = d.getUTCMonth();
11708         
11709         Roo.each(this.picker().select('.prev', true).elements, function(v){
11710             v.show();
11711             switch (this.viewMode) {
11712                 case 0:
11713
11714                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11715                         v.hide();
11716                     }
11717                     break;
11718                 case 1:
11719                 case 2:
11720                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11721                         v.hide();
11722                     }
11723                     break;
11724             }
11725         });
11726         
11727         Roo.each(this.picker().select('.next', true).elements, function(v){
11728             v.show();
11729             switch (this.viewMode) {
11730                 case 0:
11731
11732                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11733                         v.hide();
11734                     }
11735                     break;
11736                 case 1:
11737                 case 2:
11738                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11739                         v.hide();
11740                     }
11741                     break;
11742             }
11743         })
11744     },
11745     
11746     moveMonth: function(date, dir){
11747         if (!dir) return date;
11748         var new_date = new Date(date.valueOf()),
11749         day = new_date.getUTCDate(),
11750         month = new_date.getUTCMonth(),
11751         mag = Math.abs(dir),
11752         new_month, test;
11753         dir = dir > 0 ? 1 : -1;
11754         if (mag == 1){
11755             test = dir == -1
11756             // If going back one month, make sure month is not current month
11757             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11758             ? function(){
11759                 return new_date.getUTCMonth() == month;
11760             }
11761             // If going forward one month, make sure month is as expected
11762             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11763             : function(){
11764                 return new_date.getUTCMonth() != new_month;
11765             };
11766             new_month = month + dir;
11767             new_date.setUTCMonth(new_month);
11768             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11769             if (new_month < 0 || new_month > 11)
11770                 new_month = (new_month + 12) % 12;
11771         } else {
11772             // For magnitudes >1, move one month at a time...
11773             for (var i=0; i<mag; i++)
11774                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11775                 new_date = this.moveMonth(new_date, dir);
11776             // ...then reset the day, keeping it in the new month
11777             new_month = new_date.getUTCMonth();
11778             new_date.setUTCDate(day);
11779             test = function(){
11780                 return new_month != new_date.getUTCMonth();
11781             };
11782         }
11783         // Common date-resetting loop -- if date is beyond end of month, make it
11784         // end of month
11785         while (test()){
11786             new_date.setUTCDate(--day);
11787             new_date.setUTCMonth(new_month);
11788         }
11789         return new_date;
11790     },
11791
11792     moveYear: function(date, dir){
11793         return this.moveMonth(date, dir*12);
11794     },
11795
11796     dateWithinRange: function(date){
11797         return date >= this.startDate && date <= this.endDate;
11798     },
11799
11800     
11801     remove: function() {
11802         this.picker().remove();
11803     }
11804    
11805 });
11806
11807 Roo.apply(Roo.bootstrap.DateField,  {
11808     
11809     head : {
11810         tag: 'thead',
11811         cn: [
11812         {
11813             tag: 'tr',
11814             cn: [
11815             {
11816                 tag: 'th',
11817                 cls: 'prev',
11818                 html: '<i class="icon-arrow-left"/>'
11819             },
11820             {
11821                 tag: 'th',
11822                 cls: 'switch',
11823                 colspan: '5'
11824             },
11825             {
11826                 tag: 'th',
11827                 cls: 'next',
11828                 html: '<i class="icon-arrow-right"/>'
11829             }
11830
11831             ]
11832         }
11833         ]
11834     },
11835     
11836     content : {
11837         tag: 'tbody',
11838         cn: [
11839         {
11840             tag: 'tr',
11841             cn: [
11842             {
11843                 tag: 'td',
11844                 colspan: '7'
11845             }
11846             ]
11847         }
11848         ]
11849     },
11850     
11851     footer : {
11852         tag: 'tfoot',
11853         cn: [
11854         {
11855             tag: 'tr',
11856             cn: [
11857             {
11858                 tag: 'th',
11859                 colspan: '7',
11860                 cls: 'today'
11861             }
11862                     
11863             ]
11864         }
11865         ]
11866     },
11867     
11868     dates:{
11869         en: {
11870             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11871             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11872             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11873             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11874             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11875             today: "Today"
11876         }
11877     },
11878     
11879     modes: [
11880     {
11881         clsName: 'days',
11882         navFnc: 'Month',
11883         navStep: 1
11884     },
11885     {
11886         clsName: 'months',
11887         navFnc: 'FullYear',
11888         navStep: 1
11889     },
11890     {
11891         clsName: 'years',
11892         navFnc: 'FullYear',
11893         navStep: 10
11894     }]
11895 });
11896
11897 Roo.apply(Roo.bootstrap.DateField,  {
11898   
11899     template : {
11900         tag: 'div',
11901         cls: 'datepicker dropdown-menu',
11902         cn: [
11903         {
11904             tag: 'div',
11905             cls: 'datepicker-days',
11906             cn: [
11907             {
11908                 tag: 'table',
11909                 cls: 'table-condensed',
11910                 cn:[
11911                 Roo.bootstrap.DateField.head,
11912                 {
11913                     tag: 'tbody'
11914                 },
11915                 Roo.bootstrap.DateField.footer
11916                 ]
11917             }
11918             ]
11919         },
11920         {
11921             tag: 'div',
11922             cls: 'datepicker-months',
11923             cn: [
11924             {
11925                 tag: 'table',
11926                 cls: 'table-condensed',
11927                 cn:[
11928                 Roo.bootstrap.DateField.head,
11929                 Roo.bootstrap.DateField.content,
11930                 Roo.bootstrap.DateField.footer
11931                 ]
11932             }
11933             ]
11934         },
11935         {
11936             tag: 'div',
11937             cls: 'datepicker-years',
11938             cn: [
11939             {
11940                 tag: 'table',
11941                 cls: 'table-condensed',
11942                 cn:[
11943                 Roo.bootstrap.DateField.head,
11944                 Roo.bootstrap.DateField.content,
11945                 Roo.bootstrap.DateField.footer
11946                 ]
11947             }
11948             ]
11949         }
11950         ]
11951     }
11952 });
11953
11954  
11955
11956  /*
11957  * - LGPL
11958  *
11959  * TimeField
11960  * 
11961  */
11962
11963 /**
11964  * @class Roo.bootstrap.TimeField
11965  * @extends Roo.bootstrap.Input
11966  * Bootstrap DateField class
11967  * 
11968  * 
11969  * @constructor
11970  * Create a new TimeField
11971  * @param {Object} config The config object
11972  */
11973
11974 Roo.bootstrap.TimeField = function(config){
11975     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11976     this.addEvents({
11977             /**
11978              * @event show
11979              * Fires when this field show.
11980              * @param {Roo.bootstrap.DateField} this
11981              * @param {Mixed} date The date value
11982              */
11983             show : true,
11984             /**
11985              * @event show
11986              * Fires when this field hide.
11987              * @param {Roo.bootstrap.DateField} this
11988              * @param {Mixed} date The date value
11989              */
11990             hide : true,
11991             /**
11992              * @event select
11993              * Fires when select a date.
11994              * @param {Roo.bootstrap.DateField} this
11995              * @param {Mixed} date The date value
11996              */
11997             select : true
11998         });
11999 };
12000
12001 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
12002     
12003     /**
12004      * @cfg {String} format
12005      * The default time format string which can be overriden for localization support.  The format must be
12006      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12007      */
12008     format : "H:i",
12009        
12010     onRender: function(ct, position)
12011     {
12012         
12013         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12014                 
12015         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12016         
12017         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12018         
12019         this.pop = this.picker().select('>.datepicker-time',true).first();
12020         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12021         
12022         this.picker().on('mousedown', this.onMousedown, this);
12023         this.picker().on('click', this.onClick, this);
12024         
12025         this.picker().addClass('datepicker-dropdown');
12026     
12027         this.fillTime();
12028         this.update();
12029             
12030         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12031         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12032         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12033         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12034         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12035         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12036
12037     },
12038     
12039     fireKey: function(e){
12040         if (!this.picker().isVisible()){
12041             if (e.keyCode == 27) // allow escape to hide and re-show picker
12042                 this.show();
12043             return;
12044         }
12045
12046         e.preventDefault();
12047         
12048         switch(e.keyCode){
12049             case 27: // escape
12050                 this.hide();
12051                 break;
12052             case 37: // left
12053             case 39: // right
12054                 this.onTogglePeriod();
12055                 break;
12056             case 38: // up
12057                 this.onIncrementMinutes();
12058                 break;
12059             case 40: // down
12060                 this.onDecrementMinutes();
12061                 break;
12062             case 13: // enter
12063             case 9: // tab
12064                 this.setTime();
12065                 break;
12066         }
12067     },
12068     
12069     onClick: function(e) {
12070         e.stopPropagation();
12071         e.preventDefault();
12072     },
12073     
12074     picker : function()
12075     {
12076         return this.el.select('.datepicker', true).first();
12077     },
12078     
12079     fillTime: function()
12080     {    
12081         var time = this.pop.select('tbody', true).first();
12082         
12083         time.dom.innerHTML = '';
12084         
12085         time.createChild({
12086             tag: 'tr',
12087             cn: [
12088                 {
12089                     tag: 'td',
12090                     cn: [
12091                         {
12092                             tag: 'a',
12093                             href: '#',
12094                             cls: 'btn',
12095                             cn: [
12096                                 {
12097                                     tag: 'span',
12098                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12099                                 }
12100                             ]
12101                         } 
12102                     ]
12103                 },
12104                 {
12105                     tag: 'td',
12106                     cls: 'separator'
12107                 },
12108                 {
12109                     tag: 'td',
12110                     cn: [
12111                         {
12112                             tag: 'a',
12113                             href: '#',
12114                             cls: 'btn',
12115                             cn: [
12116                                 {
12117                                     tag: 'span',
12118                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12119                                 }
12120                             ]
12121                         }
12122                     ]
12123                 },
12124                 {
12125                     tag: 'td',
12126                     cls: 'separator'
12127                 }
12128             ]
12129         });
12130         
12131         time.createChild({
12132             tag: 'tr',
12133             cn: [
12134                 {
12135                     tag: 'td',
12136                     cn: [
12137                         {
12138                             tag: 'span',
12139                             cls: 'timepicker-hour',
12140                             html: '00'
12141                         }  
12142                     ]
12143                 },
12144                 {
12145                     tag: 'td',
12146                     cls: 'separator',
12147                     html: ':'
12148                 },
12149                 {
12150                     tag: 'td',
12151                     cn: [
12152                         {
12153                             tag: 'span',
12154                             cls: 'timepicker-minute',
12155                             html: '00'
12156                         }  
12157                     ]
12158                 },
12159                 {
12160                     tag: 'td',
12161                     cls: 'separator'
12162                 },
12163                 {
12164                     tag: 'td',
12165                     cn: [
12166                         {
12167                             tag: 'button',
12168                             type: 'button',
12169                             cls: 'btn btn-primary period',
12170                             html: 'AM'
12171                             
12172                         }
12173                     ]
12174                 }
12175             ]
12176         });
12177         
12178         time.createChild({
12179             tag: 'tr',
12180             cn: [
12181                 {
12182                     tag: 'td',
12183                     cn: [
12184                         {
12185                             tag: 'a',
12186                             href: '#',
12187                             cls: 'btn',
12188                             cn: [
12189                                 {
12190                                     tag: 'span',
12191                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12192                                 }
12193                             ]
12194                         }
12195                     ]
12196                 },
12197                 {
12198                     tag: 'td',
12199                     cls: 'separator'
12200                 },
12201                 {
12202                     tag: 'td',
12203                     cn: [
12204                         {
12205                             tag: 'a',
12206                             href: '#',
12207                             cls: 'btn',
12208                             cn: [
12209                                 {
12210                                     tag: 'span',
12211                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12212                                 }
12213                             ]
12214                         }
12215                     ]
12216                 },
12217                 {
12218                     tag: 'td',
12219                     cls: 'separator'
12220                 }
12221             ]
12222         });
12223         
12224     },
12225     
12226     update: function()
12227     {
12228         
12229         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12230         
12231         this.fill();
12232     },
12233     
12234     fill: function() 
12235     {
12236         var hours = this.time.getHours();
12237         var minutes = this.time.getMinutes();
12238         var period = 'AM';
12239         
12240         if(hours > 11){
12241             period = 'PM';
12242         }
12243         
12244         if(hours == 0){
12245             hours = 12;
12246         }
12247         
12248         
12249         if(hours > 12){
12250             hours = hours - 12;
12251         }
12252         
12253         if(hours < 10){
12254             hours = '0' + hours;
12255         }
12256         
12257         if(minutes < 10){
12258             minutes = '0' + minutes;
12259         }
12260         
12261         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12262         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12263         this.pop.select('button', true).first().dom.innerHTML = period;
12264         
12265     },
12266     
12267     place: function()
12268     {   
12269         this.picker().removeClass(['bottom', 'top']);
12270         
12271         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12272             /*
12273              * place to the top of element!
12274              *
12275              */
12276             
12277             this.picker().addClass('top');
12278             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12279             
12280             return;
12281         }
12282         
12283         this.picker().addClass('bottom');
12284         
12285         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12286     },
12287   
12288     onFocus : function()
12289     {
12290         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12291         this.show();
12292     },
12293     
12294     onBlur : function()
12295     {
12296         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12297         this.hide();
12298     },
12299     
12300     show : function()
12301     {
12302         this.picker().show();
12303         this.pop.show();
12304         this.update();
12305         this.place();
12306         
12307         this.fireEvent('show', this, this.date);
12308     },
12309     
12310     hide : function()
12311     {
12312         this.picker().hide();
12313         this.pop.hide();
12314         
12315         this.fireEvent('hide', this, this.date);
12316     },
12317     
12318     setTime : function()
12319     {
12320         this.hide();
12321         this.setValue(this.time.format(this.format));
12322         
12323         this.fireEvent('select', this, this.date);
12324         
12325         
12326     },
12327     
12328     onMousedown: function(e){
12329         e.stopPropagation();
12330         e.preventDefault();
12331     },
12332     
12333     onIncrementHours: function()
12334     {
12335         Roo.log('onIncrementHours');
12336         this.time = this.time.add(Date.HOUR, 1);
12337         this.update();
12338         
12339     },
12340     
12341     onDecrementHours: function()
12342     {
12343         Roo.log('onDecrementHours');
12344         this.time = this.time.add(Date.HOUR, -1);
12345         this.update();
12346     },
12347     
12348     onIncrementMinutes: function()
12349     {
12350         Roo.log('onIncrementMinutes');
12351         this.time = this.time.add(Date.MINUTE, 1);
12352         this.update();
12353     },
12354     
12355     onDecrementMinutes: function()
12356     {
12357         Roo.log('onDecrementMinutes');
12358         this.time = this.time.add(Date.MINUTE, -1);
12359         this.update();
12360     },
12361     
12362     onTogglePeriod: function()
12363     {
12364         Roo.log('onTogglePeriod');
12365         this.time = this.time.add(Date.HOUR, 12);
12366         this.update();
12367     }
12368     
12369    
12370 });
12371
12372 Roo.apply(Roo.bootstrap.TimeField,  {
12373     
12374     content : {
12375         tag: 'tbody',
12376         cn: [
12377             {
12378                 tag: 'tr',
12379                 cn: [
12380                 {
12381                     tag: 'td',
12382                     colspan: '7'
12383                 }
12384                 ]
12385             }
12386         ]
12387     },
12388     
12389     footer : {
12390         tag: 'tfoot',
12391         cn: [
12392             {
12393                 tag: 'tr',
12394                 cn: [
12395                 {
12396                     tag: 'th',
12397                     colspan: '7',
12398                     cls: '',
12399                     cn: [
12400                         {
12401                             tag: 'button',
12402                             cls: 'btn btn-info ok',
12403                             html: 'OK'
12404                         }
12405                     ]
12406                 }
12407
12408                 ]
12409             }
12410         ]
12411     }
12412 });
12413
12414 Roo.apply(Roo.bootstrap.TimeField,  {
12415   
12416     template : {
12417         tag: 'div',
12418         cls: 'datepicker dropdown-menu',
12419         cn: [
12420             {
12421                 tag: 'div',
12422                 cls: 'datepicker-time',
12423                 cn: [
12424                 {
12425                     tag: 'table',
12426                     cls: 'table-condensed',
12427                     cn:[
12428                     Roo.bootstrap.TimeField.content,
12429                     Roo.bootstrap.TimeField.footer
12430                     ]
12431                 }
12432                 ]
12433             }
12434         ]
12435     }
12436 });
12437
12438  
12439
12440  /*
12441  * - LGPL
12442  *
12443  * CheckBox
12444  * 
12445  */
12446
12447 /**
12448  * @class Roo.bootstrap.CheckBox
12449  * @extends Roo.bootstrap.Input
12450  * Bootstrap CheckBox class
12451  * 
12452  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12453  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12454  * @cfg {String} boxLabel The text that appears beside the checkbox
12455  * @cfg {Boolean} checked initnal the element
12456  * 
12457  * @constructor
12458  * Create a new CheckBox
12459  * @param {Object} config The config object
12460  */
12461
12462 Roo.bootstrap.CheckBox = function(config){
12463     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12464    
12465         this.addEvents({
12466             /**
12467             * @event check
12468             * Fires when the element is checked or unchecked.
12469             * @param {Roo.bootstrap.CheckBox} this This input
12470             * @param {Boolean} checked The new checked value
12471             */
12472            check : true
12473         });
12474 };
12475
12476 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12477     
12478     inputType: 'checkbox',
12479     inputValue: 1,
12480     valueOff: 0,
12481     boxLabel: false,
12482     checked: false,
12483     
12484     getAutoCreate : function()
12485     {
12486         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12487         
12488         var id = Roo.id();
12489         
12490         var cfg = {};
12491         
12492         cfg.cls = 'form-group' //input-group
12493         
12494         var input =  {
12495             tag: 'input',
12496             id : id,
12497             type : this.inputType,
12498             value : (!this.checked) ? this.valueOff : this.inputValue,
12499             cls : 'form-box',
12500             placeholder : this.placeholder || ''
12501             
12502         };
12503         
12504         if (this.disabled) {
12505             input.disabled=true;
12506         }
12507         
12508         if(this.checked){
12509             input.checked = this.checked;
12510         }
12511         
12512         if (this.name) {
12513             input.name = this.name;
12514         }
12515         
12516         if (this.size) {
12517             input.cls += ' input-' + this.size;
12518         }
12519         
12520         var settings=this;
12521         ['xs','sm','md','lg'].map(function(size){
12522             if (settings[size]) {
12523                 cfg.cls += ' col-' + size + '-' + settings[size];
12524             }
12525         });
12526         
12527         var inputblock = input;
12528         
12529         if (this.before || this.after) {
12530             
12531             inputblock = {
12532                 cls : 'input-group',
12533                 cn :  [] 
12534             };
12535             if (this.before) {
12536                 inputblock.cn.push({
12537                     tag :'span',
12538                     cls : 'input-group-addon',
12539                     html : this.before
12540                 });
12541             }
12542             inputblock.cn.push(input);
12543             if (this.after) {
12544                 inputblock.cn.push({
12545                     tag :'span',
12546                     cls : 'input-group-addon',
12547                     html : this.after
12548                 });
12549             }
12550             
12551         };
12552         
12553         if (align ==='left' && this.fieldLabel.length) {
12554                 Roo.log("left and has label");
12555                 cfg.cn = [
12556                     
12557                     {
12558                         tag: 'label',
12559                         'for' :  id,
12560                         cls : 'control-label col-md-' + this.labelWidth,
12561                         html : this.fieldLabel
12562                         
12563                     },
12564                     {
12565                         cls : "col-md-" + (12 - this.labelWidth), 
12566                         cn: [
12567                             inputblock
12568                         ]
12569                     }
12570                     
12571                 ];
12572         } else if ( this.fieldLabel.length) {
12573                 Roo.log(" label");
12574                 cfg.cn = [
12575                    
12576                     {
12577                         tag: this.boxLabel ? 'span' : 'label',
12578                         'for': id,
12579                         cls: 'control-label box-input-label',
12580                         //cls : 'input-group-addon',
12581                         html : this.fieldLabel
12582                         
12583                     },
12584                     
12585                     inputblock
12586                     
12587                 ];
12588
12589         } else {
12590             
12591                    Roo.log(" no label && no align");
12592                 cfg.cn = [
12593                     
12594                         inputblock
12595                     
12596                 ];
12597                 
12598                 
12599         };
12600         
12601         if(this.boxLabel){
12602             cfg.cn.push({
12603                 tag: 'label',
12604                 'for': id,
12605                 cls: 'box-label',
12606                 html: this.boxLabel
12607             })
12608         }
12609         
12610         return cfg;
12611         
12612     },
12613     
12614     /**
12615      * return the real input element.
12616      */
12617     inputEl: function ()
12618     {
12619         return this.el.select('input.form-box',true).first();
12620     },
12621     
12622     initEvents : function()
12623     {
12624 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12625         
12626         this.inputEl().on('click', this.onClick,  this);
12627         
12628     },
12629     
12630     onClick : function()
12631     {   
12632         this.setChecked(!this.checked);
12633     },
12634     
12635     setChecked : function(state,suppressEvent)
12636     {
12637         this.checked = state;
12638         
12639         this.inputEl().dom.checked = state;
12640         
12641         if(suppressEvent !== true){
12642             this.fireEvent('check', this, state);
12643         }
12644         
12645         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12646         
12647     },
12648     
12649     setValue : function(v,suppressEvent)
12650     {
12651         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12652     }
12653     
12654 });
12655
12656  
12657 /*
12658  * - LGPL
12659  *
12660  * Radio
12661  * 
12662  */
12663
12664 /**
12665  * @class Roo.bootstrap.Radio
12666  * @extends Roo.bootstrap.CheckBox
12667  * Bootstrap Radio class
12668
12669  * @constructor
12670  * Create a new Radio
12671  * @param {Object} config The config object
12672  */
12673
12674 Roo.bootstrap.Radio = function(config){
12675     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12676    
12677 };
12678
12679 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12680     
12681     inputType: 'radio',
12682     inputValue: '',
12683     valueOff: '',
12684     
12685     getAutoCreate : function()
12686     {
12687         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12688         
12689         var id = Roo.id();
12690         
12691         var cfg = {};
12692         
12693         cfg.cls = 'form-group' //input-group
12694         
12695         var input =  {
12696             tag: 'input',
12697             id : id,
12698             type : this.inputType,
12699             value : (!this.checked) ? this.valueOff : this.inputValue,
12700             cls : 'form-box',
12701             placeholder : this.placeholder || ''
12702             
12703         };
12704         
12705         if (this.disabled) {
12706             input.disabled=true;
12707         }
12708         
12709         if(this.checked){
12710             input.checked = this.checked;
12711         }
12712         
12713         if (this.name) {
12714             input.name = this.name;
12715         }
12716         
12717         if (this.size) {
12718             input.cls += ' input-' + this.size;
12719         }
12720         
12721         var settings=this;
12722         ['xs','sm','md','lg'].map(function(size){
12723             if (settings[size]) {
12724                 cfg.cls += ' col-' + size + '-' + settings[size];
12725             }
12726         });
12727         
12728         var inputblock = input;
12729         
12730         if (this.before || this.after) {
12731             
12732             inputblock = {
12733                 cls : 'input-group',
12734                 cn :  [] 
12735             };
12736             if (this.before) {
12737                 inputblock.cn.push({
12738                     tag :'span',
12739                     cls : 'input-group-addon',
12740                     html : this.before
12741                 });
12742             }
12743             inputblock.cn.push(input);
12744             if (this.after) {
12745                 inputblock.cn.push({
12746                     tag :'span',
12747                     cls : 'input-group-addon',
12748                     html : this.after
12749                 });
12750             }
12751             
12752         };
12753         
12754         if (align ==='left' && this.fieldLabel.length) {
12755                 Roo.log("left and has label");
12756                 cfg.cn = [
12757                     
12758                     {
12759                         tag: 'label',
12760                         'for' :  id,
12761                         cls : 'control-label col-md-' + this.labelWidth,
12762                         html : this.fieldLabel
12763                         
12764                     },
12765                     {
12766                         cls : "col-md-" + (12 - this.labelWidth), 
12767                         cn: [
12768                             inputblock
12769                         ]
12770                     }
12771                     
12772                 ];
12773         } else if ( this.fieldLabel.length) {
12774                 Roo.log(" label");
12775                  cfg.cn = [
12776                    
12777                     {
12778                         tag: 'label',
12779                         'for': id,
12780                         cls: 'control-label box-input-label',
12781                         //cls : 'input-group-addon',
12782                         html : this.fieldLabel
12783                         
12784                     },
12785                     
12786                     inputblock
12787                     
12788                 ];
12789
12790         } else {
12791             
12792                    Roo.log(" no label && no align");
12793                 cfg.cn = [
12794                     
12795                         inputblock
12796                     
12797                 ];
12798                 
12799                 
12800         };
12801         
12802         if(this.boxLabel){
12803             cfg.cn.push({
12804                 tag: 'label',
12805                 'for': id,
12806                 cls: 'box-label',
12807                 html: this.boxLabel
12808             })
12809         }
12810         
12811         return cfg;
12812         
12813     },
12814    
12815     onClick : function()
12816     {   
12817         this.setChecked(true);
12818     },
12819     
12820     setChecked : function(state,suppressEvent)
12821     {
12822         if(state){
12823             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12824                 v.dom.checked = false;
12825             });
12826         }
12827         
12828         this.checked = state;
12829         this.inputEl().dom.checked = state;
12830         
12831         if(suppressEvent !== true){
12832             this.fireEvent('check', this, state);
12833         }
12834         
12835         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12836         
12837     },
12838     
12839     getGroupValue : function()
12840     {
12841         var value = ''
12842         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12843             if(v.dom.checked == true){
12844                 value = v.dom.value;
12845             }
12846         });
12847         
12848         return value;
12849     },
12850     
12851     /**
12852      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12853      * @return {Mixed} value The field value
12854      */
12855     getValue : function(){
12856         return this.getGroupValue();
12857     }
12858     
12859 });
12860
12861  
12862 //<script type="text/javascript">
12863
12864 /*
12865  * Based  Ext JS Library 1.1.1
12866  * Copyright(c) 2006-2007, Ext JS, LLC.
12867  * LGPL
12868  *
12869  */
12870  
12871 /**
12872  * @class Roo.HtmlEditorCore
12873  * @extends Roo.Component
12874  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12875  *
12876  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12877  */
12878
12879 Roo.HtmlEditorCore = function(config){
12880     
12881     
12882     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12883     this.addEvents({
12884         /**
12885          * @event initialize
12886          * Fires when the editor is fully initialized (including the iframe)
12887          * @param {Roo.HtmlEditorCore} this
12888          */
12889         initialize: true,
12890         /**
12891          * @event activate
12892          * Fires when the editor is first receives the focus. Any insertion must wait
12893          * until after this event.
12894          * @param {Roo.HtmlEditorCore} this
12895          */
12896         activate: true,
12897          /**
12898          * @event beforesync
12899          * Fires before the textarea is updated with content from the editor iframe. Return false
12900          * to cancel the sync.
12901          * @param {Roo.HtmlEditorCore} this
12902          * @param {String} html
12903          */
12904         beforesync: true,
12905          /**
12906          * @event beforepush
12907          * Fires before the iframe editor is updated with content from the textarea. Return false
12908          * to cancel the push.
12909          * @param {Roo.HtmlEditorCore} this
12910          * @param {String} html
12911          */
12912         beforepush: true,
12913          /**
12914          * @event sync
12915          * Fires when the textarea is updated with content from the editor iframe.
12916          * @param {Roo.HtmlEditorCore} this
12917          * @param {String} html
12918          */
12919         sync: true,
12920          /**
12921          * @event push
12922          * Fires when the iframe editor is updated with content from the textarea.
12923          * @param {Roo.HtmlEditorCore} this
12924          * @param {String} html
12925          */
12926         push: true,
12927         
12928         /**
12929          * @event editorevent
12930          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12931          * @param {Roo.HtmlEditorCore} this
12932          */
12933         editorevent: true
12934     });
12935      
12936 };
12937
12938
12939 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12940
12941
12942      /**
12943      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12944      */
12945     
12946     owner : false,
12947     
12948      /**
12949      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12950      *                        Roo.resizable.
12951      */
12952     resizable : false,
12953      /**
12954      * @cfg {Number} height (in pixels)
12955      */   
12956     height: 300,
12957    /**
12958      * @cfg {Number} width (in pixels)
12959      */   
12960     width: 500,
12961     
12962     /**
12963      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12964      * 
12965      */
12966     stylesheets: false,
12967     
12968     // id of frame..
12969     frameId: false,
12970     
12971     // private properties
12972     validationEvent : false,
12973     deferHeight: true,
12974     initialized : false,
12975     activated : false,
12976     sourceEditMode : false,
12977     onFocus : Roo.emptyFn,
12978     iframePad:3,
12979     hideMode:'offsets',
12980     
12981     clearUp: true,
12982     
12983      
12984     
12985
12986     /**
12987      * Protected method that will not generally be called directly. It
12988      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12989      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12990      */
12991     getDocMarkup : function(){
12992         // body styles..
12993         var st = '';
12994         Roo.log(this.stylesheets);
12995         
12996         // inherit styels from page...?? 
12997         if (this.stylesheets === false) {
12998             
12999             Roo.get(document.head).select('style').each(function(node) {
13000                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13001             });
13002             
13003             Roo.get(document.head).select('link').each(function(node) { 
13004                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13005             });
13006             
13007         } else if (!this.stylesheets.length) {
13008                 // simple..
13009                 st = '<style type="text/css">' +
13010                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13011                    '</style>';
13012         } else {
13013             Roo.each(this.stylesheets, function(s) {
13014                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13015             });
13016             
13017         }
13018         
13019         st +=  '<style type="text/css">' +
13020             'IMG { cursor: pointer } ' +
13021         '</style>';
13022
13023         
13024         return '<html><head>' + st  +
13025             //<style type="text/css">' +
13026             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13027             //'</style>' +
13028             ' </head><body class="roo-htmleditor-body"></body></html>';
13029     },
13030
13031     // private
13032     onRender : function(ct, position)
13033     {
13034         var _t = this;
13035         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13036         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13037         
13038         
13039         this.el.dom.style.border = '0 none';
13040         this.el.dom.setAttribute('tabIndex', -1);
13041         this.el.addClass('x-hidden hide');
13042         
13043         
13044         
13045         if(Roo.isIE){ // fix IE 1px bogus margin
13046             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13047         }
13048        
13049         
13050         this.frameId = Roo.id();
13051         
13052          
13053         
13054         var iframe = this.owner.wrap.createChild({
13055             tag: 'iframe',
13056             cls: 'form-control', // bootstrap..
13057             id: this.frameId,
13058             name: this.frameId,
13059             frameBorder : 'no',
13060             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13061         }, this.el
13062         );
13063         
13064         
13065         this.iframe = iframe.dom;
13066
13067          this.assignDocWin();
13068         
13069         this.doc.designMode = 'on';
13070        
13071         this.doc.open();
13072         this.doc.write(this.getDocMarkup());
13073         this.doc.close();
13074
13075         
13076         var task = { // must defer to wait for browser to be ready
13077             run : function(){
13078                 //console.log("run task?" + this.doc.readyState);
13079                 this.assignDocWin();
13080                 if(this.doc.body || this.doc.readyState == 'complete'){
13081                     try {
13082                         this.doc.designMode="on";
13083                     } catch (e) {
13084                         return;
13085                     }
13086                     Roo.TaskMgr.stop(task);
13087                     this.initEditor.defer(10, this);
13088                 }
13089             },
13090             interval : 10,
13091             duration: 10000,
13092             scope: this
13093         };
13094         Roo.TaskMgr.start(task);
13095
13096         
13097          
13098     },
13099
13100     // private
13101     onResize : function(w, h)
13102     {
13103          Roo.log('resize: ' +w + ',' + h );
13104         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13105         if(!this.iframe){
13106             return;
13107         }
13108         if(typeof w == 'number'){
13109             
13110             this.iframe.style.width = w + 'px';
13111         }
13112         if(typeof h == 'number'){
13113             
13114             this.iframe.style.height = h + 'px';
13115             if(this.doc){
13116                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13117             }
13118         }
13119         
13120     },
13121
13122     /**
13123      * Toggles the editor between standard and source edit mode.
13124      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13125      */
13126     toggleSourceEdit : function(sourceEditMode){
13127         
13128         this.sourceEditMode = sourceEditMode === true;
13129         
13130         if(this.sourceEditMode){
13131  
13132             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13133             
13134         }else{
13135             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13136             //this.iframe.className = '';
13137             this.deferFocus();
13138         }
13139         //this.setSize(this.owner.wrap.getSize());
13140         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13141     },
13142
13143     
13144   
13145
13146     /**
13147      * Protected method that will not generally be called directly. If you need/want
13148      * custom HTML cleanup, this is the method you should override.
13149      * @param {String} html The HTML to be cleaned
13150      * return {String} The cleaned HTML
13151      */
13152     cleanHtml : function(html){
13153         html = String(html);
13154         if(html.length > 5){
13155             if(Roo.isSafari){ // strip safari nonsense
13156                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13157             }
13158         }
13159         if(html == '&nbsp;'){
13160             html = '';
13161         }
13162         return html;
13163     },
13164
13165     /**
13166      * HTML Editor -> Textarea
13167      * Protected method that will not generally be called directly. Syncs the contents
13168      * of the editor iframe with the textarea.
13169      */
13170     syncValue : function(){
13171         if(this.initialized){
13172             var bd = (this.doc.body || this.doc.documentElement);
13173             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13174             var html = bd.innerHTML;
13175             if(Roo.isSafari){
13176                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13177                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13178                 if(m && m[1]){
13179                     html = '<div style="'+m[0]+'">' + html + '</div>';
13180                 }
13181             }
13182             html = this.cleanHtml(html);
13183             // fix up the special chars.. normaly like back quotes in word...
13184             // however we do not want to do this with chinese..
13185             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13186                 var cc = b.charCodeAt();
13187                 if (
13188                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13189                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13190                     (cc >= 0xf900 && cc < 0xfb00 )
13191                 ) {
13192                         return b;
13193                 }
13194                 return "&#"+cc+";" 
13195             });
13196             if(this.owner.fireEvent('beforesync', this, html) !== false){
13197                 this.el.dom.value = html;
13198                 this.owner.fireEvent('sync', this, html);
13199             }
13200         }
13201     },
13202
13203     /**
13204      * Protected method that will not generally be called directly. Pushes the value of the textarea
13205      * into the iframe editor.
13206      */
13207     pushValue : function(){
13208         if(this.initialized){
13209             var v = this.el.dom.value.trim();
13210             
13211 //            if(v.length < 1){
13212 //                v = '&#160;';
13213 //            }
13214             
13215             if(this.owner.fireEvent('beforepush', this, v) !== false){
13216                 var d = (this.doc.body || this.doc.documentElement);
13217                 d.innerHTML = v;
13218                 this.cleanUpPaste();
13219                 this.el.dom.value = d.innerHTML;
13220                 this.owner.fireEvent('push', this, v);
13221             }
13222         }
13223     },
13224
13225     // private
13226     deferFocus : function(){
13227         this.focus.defer(10, this);
13228     },
13229
13230     // doc'ed in Field
13231     focus : function(){
13232         if(this.win && !this.sourceEditMode){
13233             this.win.focus();
13234         }else{
13235             this.el.focus();
13236         }
13237     },
13238     
13239     assignDocWin: function()
13240     {
13241         var iframe = this.iframe;
13242         
13243          if(Roo.isIE){
13244             this.doc = iframe.contentWindow.document;
13245             this.win = iframe.contentWindow;
13246         } else {
13247             if (!Roo.get(this.frameId)) {
13248                 return;
13249             }
13250             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13251             this.win = Roo.get(this.frameId).dom.contentWindow;
13252         }
13253     },
13254     
13255     // private
13256     initEditor : function(){
13257         //console.log("INIT EDITOR");
13258         this.assignDocWin();
13259         
13260         
13261         
13262         this.doc.designMode="on";
13263         this.doc.open();
13264         this.doc.write(this.getDocMarkup());
13265         this.doc.close();
13266         
13267         var dbody = (this.doc.body || this.doc.documentElement);
13268         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13269         // this copies styles from the containing element into thsi one..
13270         // not sure why we need all of this..
13271         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13272         ss['background-attachment'] = 'fixed'; // w3c
13273         dbody.bgProperties = 'fixed'; // ie
13274         Roo.DomHelper.applyStyles(dbody, ss);
13275         Roo.EventManager.on(this.doc, {
13276             //'mousedown': this.onEditorEvent,
13277             'mouseup': this.onEditorEvent,
13278             'dblclick': this.onEditorEvent,
13279             'click': this.onEditorEvent,
13280             'keyup': this.onEditorEvent,
13281             buffer:100,
13282             scope: this
13283         });
13284         if(Roo.isGecko){
13285             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13286         }
13287         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13288             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13289         }
13290         this.initialized = true;
13291
13292         this.owner.fireEvent('initialize', this);
13293         this.pushValue();
13294     },
13295
13296     // private
13297     onDestroy : function(){
13298         
13299         
13300         
13301         if(this.rendered){
13302             
13303             //for (var i =0; i < this.toolbars.length;i++) {
13304             //    // fixme - ask toolbars for heights?
13305             //    this.toolbars[i].onDestroy();
13306            // }
13307             
13308             //this.wrap.dom.innerHTML = '';
13309             //this.wrap.remove();
13310         }
13311     },
13312
13313     // private
13314     onFirstFocus : function(){
13315         
13316         this.assignDocWin();
13317         
13318         
13319         this.activated = true;
13320          
13321     
13322         if(Roo.isGecko){ // prevent silly gecko errors
13323             this.win.focus();
13324             var s = this.win.getSelection();
13325             if(!s.focusNode || s.focusNode.nodeType != 3){
13326                 var r = s.getRangeAt(0);
13327                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13328                 r.collapse(true);
13329                 this.deferFocus();
13330             }
13331             try{
13332                 this.execCmd('useCSS', true);
13333                 this.execCmd('styleWithCSS', false);
13334             }catch(e){}
13335         }
13336         this.owner.fireEvent('activate', this);
13337     },
13338
13339     // private
13340     adjustFont: function(btn){
13341         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13342         //if(Roo.isSafari){ // safari
13343         //    adjust *= 2;
13344        // }
13345         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13346         if(Roo.isSafari){ // safari
13347             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13348             v =  (v < 10) ? 10 : v;
13349             v =  (v > 48) ? 48 : v;
13350             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13351             
13352         }
13353         
13354         
13355         v = Math.max(1, v+adjust);
13356         
13357         this.execCmd('FontSize', v  );
13358     },
13359
13360     onEditorEvent : function(e){
13361         this.owner.fireEvent('editorevent', this, e);
13362       //  this.updateToolbar();
13363         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13364     },
13365
13366     insertTag : function(tg)
13367     {
13368         // could be a bit smarter... -> wrap the current selected tRoo..
13369         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13370             
13371             range = this.createRange(this.getSelection());
13372             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13373             wrappingNode.appendChild(range.extractContents());
13374             range.insertNode(wrappingNode);
13375
13376             return;
13377             
13378             
13379             
13380         }
13381         this.execCmd("formatblock",   tg);
13382         
13383     },
13384     
13385     insertText : function(txt)
13386     {
13387         
13388         
13389         var range = this.createRange();
13390         range.deleteContents();
13391                //alert(Sender.getAttribute('label'));
13392                
13393         range.insertNode(this.doc.createTextNode(txt));
13394     } ,
13395     
13396      
13397
13398     /**
13399      * Executes a Midas editor command on the editor document and performs necessary focus and
13400      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13401      * @param {String} cmd The Midas command
13402      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13403      */
13404     relayCmd : function(cmd, value){
13405         this.win.focus();
13406         this.execCmd(cmd, value);
13407         this.owner.fireEvent('editorevent', this);
13408         //this.updateToolbar();
13409         this.owner.deferFocus();
13410     },
13411
13412     /**
13413      * Executes a Midas editor command directly on the editor document.
13414      * For visual commands, you should use {@link #relayCmd} instead.
13415      * <b>This should only be called after the editor is initialized.</b>
13416      * @param {String} cmd The Midas command
13417      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13418      */
13419     execCmd : function(cmd, value){
13420         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13421         this.syncValue();
13422     },
13423  
13424  
13425    
13426     /**
13427      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13428      * to insert tRoo.
13429      * @param {String} text | dom node.. 
13430      */
13431     insertAtCursor : function(text)
13432     {
13433         
13434         
13435         
13436         if(!this.activated){
13437             return;
13438         }
13439         /*
13440         if(Roo.isIE){
13441             this.win.focus();
13442             var r = this.doc.selection.createRange();
13443             if(r){
13444                 r.collapse(true);
13445                 r.pasteHTML(text);
13446                 this.syncValue();
13447                 this.deferFocus();
13448             
13449             }
13450             return;
13451         }
13452         */
13453         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13454             this.win.focus();
13455             
13456             
13457             // from jquery ui (MIT licenced)
13458             var range, node;
13459             var win = this.win;
13460             
13461             if (win.getSelection && win.getSelection().getRangeAt) {
13462                 range = win.getSelection().getRangeAt(0);
13463                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13464                 range.insertNode(node);
13465             } else if (win.document.selection && win.document.selection.createRange) {
13466                 // no firefox support
13467                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13468                 win.document.selection.createRange().pasteHTML(txt);
13469             } else {
13470                 // no firefox support
13471                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13472                 this.execCmd('InsertHTML', txt);
13473             } 
13474             
13475             this.syncValue();
13476             
13477             this.deferFocus();
13478         }
13479     },
13480  // private
13481     mozKeyPress : function(e){
13482         if(e.ctrlKey){
13483             var c = e.getCharCode(), cmd;
13484           
13485             if(c > 0){
13486                 c = String.fromCharCode(c).toLowerCase();
13487                 switch(c){
13488                     case 'b':
13489                         cmd = 'bold';
13490                         break;
13491                     case 'i':
13492                         cmd = 'italic';
13493                         break;
13494                     
13495                     case 'u':
13496                         cmd = 'underline';
13497                         break;
13498                     
13499                     case 'v':
13500                         this.cleanUpPaste.defer(100, this);
13501                         return;
13502                         
13503                 }
13504                 if(cmd){
13505                     this.win.focus();
13506                     this.execCmd(cmd);
13507                     this.deferFocus();
13508                     e.preventDefault();
13509                 }
13510                 
13511             }
13512         }
13513     },
13514
13515     // private
13516     fixKeys : function(){ // load time branching for fastest keydown performance
13517         if(Roo.isIE){
13518             return function(e){
13519                 var k = e.getKey(), r;
13520                 if(k == e.TAB){
13521                     e.stopEvent();
13522                     r = this.doc.selection.createRange();
13523                     if(r){
13524                         r.collapse(true);
13525                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13526                         this.deferFocus();
13527                     }
13528                     return;
13529                 }
13530                 
13531                 if(k == e.ENTER){
13532                     r = this.doc.selection.createRange();
13533                     if(r){
13534                         var target = r.parentElement();
13535                         if(!target || target.tagName.toLowerCase() != 'li'){
13536                             e.stopEvent();
13537                             r.pasteHTML('<br />');
13538                             r.collapse(false);
13539                             r.select();
13540                         }
13541                     }
13542                 }
13543                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13544                     this.cleanUpPaste.defer(100, this);
13545                     return;
13546                 }
13547                 
13548                 
13549             };
13550         }else if(Roo.isOpera){
13551             return function(e){
13552                 var k = e.getKey();
13553                 if(k == e.TAB){
13554                     e.stopEvent();
13555                     this.win.focus();
13556                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13557                     this.deferFocus();
13558                 }
13559                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13560                     this.cleanUpPaste.defer(100, this);
13561                     return;
13562                 }
13563                 
13564             };
13565         }else if(Roo.isSafari){
13566             return function(e){
13567                 var k = e.getKey();
13568                 
13569                 if(k == e.TAB){
13570                     e.stopEvent();
13571                     this.execCmd('InsertText','\t');
13572                     this.deferFocus();
13573                     return;
13574                 }
13575                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13576                     this.cleanUpPaste.defer(100, this);
13577                     return;
13578                 }
13579                 
13580              };
13581         }
13582     }(),
13583     
13584     getAllAncestors: function()
13585     {
13586         var p = this.getSelectedNode();
13587         var a = [];
13588         if (!p) {
13589             a.push(p); // push blank onto stack..
13590             p = this.getParentElement();
13591         }
13592         
13593         
13594         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13595             a.push(p);
13596             p = p.parentNode;
13597         }
13598         a.push(this.doc.body);
13599         return a;
13600     },
13601     lastSel : false,
13602     lastSelNode : false,
13603     
13604     
13605     getSelection : function() 
13606     {
13607         this.assignDocWin();
13608         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13609     },
13610     
13611     getSelectedNode: function() 
13612     {
13613         // this may only work on Gecko!!!
13614         
13615         // should we cache this!!!!
13616         
13617         
13618         
13619          
13620         var range = this.createRange(this.getSelection()).cloneRange();
13621         
13622         if (Roo.isIE) {
13623             var parent = range.parentElement();
13624             while (true) {
13625                 var testRange = range.duplicate();
13626                 testRange.moveToElementText(parent);
13627                 if (testRange.inRange(range)) {
13628                     break;
13629                 }
13630                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13631                     break;
13632                 }
13633                 parent = parent.parentElement;
13634             }
13635             return parent;
13636         }
13637         
13638         // is ancestor a text element.
13639         var ac =  range.commonAncestorContainer;
13640         if (ac.nodeType == 3) {
13641             ac = ac.parentNode;
13642         }
13643         
13644         var ar = ac.childNodes;
13645          
13646         var nodes = [];
13647         var other_nodes = [];
13648         var has_other_nodes = false;
13649         for (var i=0;i<ar.length;i++) {
13650             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13651                 continue;
13652             }
13653             // fullly contained node.
13654             
13655             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13656                 nodes.push(ar[i]);
13657                 continue;
13658             }
13659             
13660             // probably selected..
13661             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13662                 other_nodes.push(ar[i]);
13663                 continue;
13664             }
13665             // outer..
13666             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13667                 continue;
13668             }
13669             
13670             
13671             has_other_nodes = true;
13672         }
13673         if (!nodes.length && other_nodes.length) {
13674             nodes= other_nodes;
13675         }
13676         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13677             return false;
13678         }
13679         
13680         return nodes[0];
13681     },
13682     createRange: function(sel)
13683     {
13684         // this has strange effects when using with 
13685         // top toolbar - not sure if it's a great idea.
13686         //this.editor.contentWindow.focus();
13687         if (typeof sel != "undefined") {
13688             try {
13689                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13690             } catch(e) {
13691                 return this.doc.createRange();
13692             }
13693         } else {
13694             return this.doc.createRange();
13695         }
13696     },
13697     getParentElement: function()
13698     {
13699         
13700         this.assignDocWin();
13701         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13702         
13703         var range = this.createRange(sel);
13704          
13705         try {
13706             var p = range.commonAncestorContainer;
13707             while (p.nodeType == 3) { // text node
13708                 p = p.parentNode;
13709             }
13710             return p;
13711         } catch (e) {
13712             return null;
13713         }
13714     
13715     },
13716     /***
13717      *
13718      * Range intersection.. the hard stuff...
13719      *  '-1' = before
13720      *  '0' = hits..
13721      *  '1' = after.
13722      *         [ -- selected range --- ]
13723      *   [fail]                        [fail]
13724      *
13725      *    basically..
13726      *      if end is before start or  hits it. fail.
13727      *      if start is after end or hits it fail.
13728      *
13729      *   if either hits (but other is outside. - then it's not 
13730      *   
13731      *    
13732      **/
13733     
13734     
13735     // @see http://www.thismuchiknow.co.uk/?p=64.
13736     rangeIntersectsNode : function(range, node)
13737     {
13738         var nodeRange = node.ownerDocument.createRange();
13739         try {
13740             nodeRange.selectNode(node);
13741         } catch (e) {
13742             nodeRange.selectNodeContents(node);
13743         }
13744     
13745         var rangeStartRange = range.cloneRange();
13746         rangeStartRange.collapse(true);
13747     
13748         var rangeEndRange = range.cloneRange();
13749         rangeEndRange.collapse(false);
13750     
13751         var nodeStartRange = nodeRange.cloneRange();
13752         nodeStartRange.collapse(true);
13753     
13754         var nodeEndRange = nodeRange.cloneRange();
13755         nodeEndRange.collapse(false);
13756     
13757         return rangeStartRange.compareBoundaryPoints(
13758                  Range.START_TO_START, nodeEndRange) == -1 &&
13759                rangeEndRange.compareBoundaryPoints(
13760                  Range.START_TO_START, nodeStartRange) == 1;
13761         
13762          
13763     },
13764     rangeCompareNode : function(range, node)
13765     {
13766         var nodeRange = node.ownerDocument.createRange();
13767         try {
13768             nodeRange.selectNode(node);
13769         } catch (e) {
13770             nodeRange.selectNodeContents(node);
13771         }
13772         
13773         
13774         range.collapse(true);
13775     
13776         nodeRange.collapse(true);
13777      
13778         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13779         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13780          
13781         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13782         
13783         var nodeIsBefore   =  ss == 1;
13784         var nodeIsAfter    = ee == -1;
13785         
13786         if (nodeIsBefore && nodeIsAfter)
13787             return 0; // outer
13788         if (!nodeIsBefore && nodeIsAfter)
13789             return 1; //right trailed.
13790         
13791         if (nodeIsBefore && !nodeIsAfter)
13792             return 2;  // left trailed.
13793         // fully contined.
13794         return 3;
13795     },
13796
13797     // private? - in a new class?
13798     cleanUpPaste :  function()
13799     {
13800         // cleans up the whole document..
13801         Roo.log('cleanuppaste');
13802         
13803         this.cleanUpChildren(this.doc.body);
13804         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13805         if (clean != this.doc.body.innerHTML) {
13806             this.doc.body.innerHTML = clean;
13807         }
13808         
13809     },
13810     
13811     cleanWordChars : function(input) {// change the chars to hex code
13812         var he = Roo.HtmlEditorCore;
13813         
13814         var output = input;
13815         Roo.each(he.swapCodes, function(sw) { 
13816             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13817             
13818             output = output.replace(swapper, sw[1]);
13819         });
13820         
13821         return output;
13822     },
13823     
13824     
13825     cleanUpChildren : function (n)
13826     {
13827         if (!n.childNodes.length) {
13828             return;
13829         }
13830         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13831            this.cleanUpChild(n.childNodes[i]);
13832         }
13833     },
13834     
13835     
13836         
13837     
13838     cleanUpChild : function (node)
13839     {
13840         var ed = this;
13841         //console.log(node);
13842         if (node.nodeName == "#text") {
13843             // clean up silly Windows -- stuff?
13844             return; 
13845         }
13846         if (node.nodeName == "#comment") {
13847             node.parentNode.removeChild(node);
13848             // clean up silly Windows -- stuff?
13849             return; 
13850         }
13851         
13852         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13853             // remove node.
13854             node.parentNode.removeChild(node);
13855             return;
13856             
13857         }
13858         
13859         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13860         
13861         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13862         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13863         
13864         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13865         //    remove_keep_children = true;
13866         //}
13867         
13868         if (remove_keep_children) {
13869             this.cleanUpChildren(node);
13870             // inserts everything just before this node...
13871             while (node.childNodes.length) {
13872                 var cn = node.childNodes[0];
13873                 node.removeChild(cn);
13874                 node.parentNode.insertBefore(cn, node);
13875             }
13876             node.parentNode.removeChild(node);
13877             return;
13878         }
13879         
13880         if (!node.attributes || !node.attributes.length) {
13881             this.cleanUpChildren(node);
13882             return;
13883         }
13884         
13885         function cleanAttr(n,v)
13886         {
13887             
13888             if (v.match(/^\./) || v.match(/^\//)) {
13889                 return;
13890             }
13891             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13892                 return;
13893             }
13894             if (v.match(/^#/)) {
13895                 return;
13896             }
13897 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13898             node.removeAttribute(n);
13899             
13900         }
13901         
13902         function cleanStyle(n,v)
13903         {
13904             if (v.match(/expression/)) { //XSS?? should we even bother..
13905                 node.removeAttribute(n);
13906                 return;
13907             }
13908             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13909             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13910             
13911             
13912             var parts = v.split(/;/);
13913             var clean = [];
13914             
13915             Roo.each(parts, function(p) {
13916                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13917                 if (!p.length) {
13918                     return true;
13919                 }
13920                 var l = p.split(':').shift().replace(/\s+/g,'');
13921                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13922                 
13923                 if ( cblack.indexOf(l) > -1) {
13924 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13925                     //node.removeAttribute(n);
13926                     return true;
13927                 }
13928                 //Roo.log()
13929                 // only allow 'c whitelisted system attributes'
13930                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13931 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13932                     //node.removeAttribute(n);
13933                     return true;
13934                 }
13935                 
13936                 
13937                  
13938                 
13939                 clean.push(p);
13940                 return true;
13941             });
13942             if (clean.length) { 
13943                 node.setAttribute(n, clean.join(';'));
13944             } else {
13945                 node.removeAttribute(n);
13946             }
13947             
13948         }
13949         
13950         
13951         for (var i = node.attributes.length-1; i > -1 ; i--) {
13952             var a = node.attributes[i];
13953             //console.log(a);
13954             
13955             if (a.name.toLowerCase().substr(0,2)=='on')  {
13956                 node.removeAttribute(a.name);
13957                 continue;
13958             }
13959             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13960                 node.removeAttribute(a.name);
13961                 continue;
13962             }
13963             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13964                 cleanAttr(a.name,a.value); // fixme..
13965                 continue;
13966             }
13967             if (a.name == 'style') {
13968                 cleanStyle(a.name,a.value);
13969                 continue;
13970             }
13971             /// clean up MS crap..
13972             // tecnically this should be a list of valid class'es..
13973             
13974             
13975             if (a.name == 'class') {
13976                 if (a.value.match(/^Mso/)) {
13977                     node.className = '';
13978                 }
13979                 
13980                 if (a.value.match(/body/)) {
13981                     node.className = '';
13982                 }
13983                 continue;
13984             }
13985             
13986             // style cleanup!?
13987             // class cleanup?
13988             
13989         }
13990         
13991         
13992         this.cleanUpChildren(node);
13993         
13994         
13995     }
13996     
13997     
13998     // hide stuff that is not compatible
13999     /**
14000      * @event blur
14001      * @hide
14002      */
14003     /**
14004      * @event change
14005      * @hide
14006      */
14007     /**
14008      * @event focus
14009      * @hide
14010      */
14011     /**
14012      * @event specialkey
14013      * @hide
14014      */
14015     /**
14016      * @cfg {String} fieldClass @hide
14017      */
14018     /**
14019      * @cfg {String} focusClass @hide
14020      */
14021     /**
14022      * @cfg {String} autoCreate @hide
14023      */
14024     /**
14025      * @cfg {String} inputType @hide
14026      */
14027     /**
14028      * @cfg {String} invalidClass @hide
14029      */
14030     /**
14031      * @cfg {String} invalidText @hide
14032      */
14033     /**
14034      * @cfg {String} msgFx @hide
14035      */
14036     /**
14037      * @cfg {String} validateOnBlur @hide
14038      */
14039 });
14040
14041 Roo.HtmlEditorCore.white = [
14042         'area', 'br', 'img', 'input', 'hr', 'wbr',
14043         
14044        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14045        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14046        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14047        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14048        'table',   'ul',         'xmp', 
14049        
14050        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14051       'thead',   'tr', 
14052      
14053       'dir', 'menu', 'ol', 'ul', 'dl',
14054        
14055       'embed',  'object'
14056 ];
14057
14058
14059 Roo.HtmlEditorCore.black = [
14060     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14061         'applet', // 
14062         'base',   'basefont', 'bgsound', 'blink',  'body', 
14063         'frame',  'frameset', 'head',    'html',   'ilayer', 
14064         'iframe', 'layer',  'link',     'meta',    'object',   
14065         'script', 'style' ,'title',  'xml' // clean later..
14066 ];
14067 Roo.HtmlEditorCore.clean = [
14068     'script', 'style', 'title', 'xml'
14069 ];
14070 Roo.HtmlEditorCore.remove = [
14071     'font'
14072 ];
14073 // attributes..
14074
14075 Roo.HtmlEditorCore.ablack = [
14076     'on'
14077 ];
14078     
14079 Roo.HtmlEditorCore.aclean = [ 
14080     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14081 ];
14082
14083 // protocols..
14084 Roo.HtmlEditorCore.pwhite= [
14085         'http',  'https',  'mailto'
14086 ];
14087
14088 // white listed style attributes.
14089 Roo.HtmlEditorCore.cwhite= [
14090       //  'text-align', /// default is to allow most things..
14091       
14092          
14093 //        'font-size'//??
14094 ];
14095
14096 // black listed style attributes.
14097 Roo.HtmlEditorCore.cblack= [
14098       //  'font-size' -- this can be set by the project 
14099 ];
14100
14101
14102 Roo.HtmlEditorCore.swapCodes   =[ 
14103     [    8211, "--" ], 
14104     [    8212, "--" ], 
14105     [    8216,  "'" ],  
14106     [    8217, "'" ],  
14107     [    8220, '"' ],  
14108     [    8221, '"' ],  
14109     [    8226, "*" ],  
14110     [    8230, "..." ]
14111 ]; 
14112
14113     /*
14114  * - LGPL
14115  *
14116  * HtmlEditor
14117  * 
14118  */
14119
14120 /**
14121  * @class Roo.bootstrap.HtmlEditor
14122  * @extends Roo.bootstrap.TextArea
14123  * Bootstrap HtmlEditor class
14124
14125  * @constructor
14126  * Create a new HtmlEditor
14127  * @param {Object} config The config object
14128  */
14129
14130 Roo.bootstrap.HtmlEditor = function(config){
14131     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14132     if (!this.toolbars) {
14133         this.toolbars = [];
14134     }
14135     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14136     this.addEvents({
14137             /**
14138              * @event initialize
14139              * Fires when the editor is fully initialized (including the iframe)
14140              * @param {HtmlEditor} this
14141              */
14142             initialize: true,
14143             /**
14144              * @event activate
14145              * Fires when the editor is first receives the focus. Any insertion must wait
14146              * until after this event.
14147              * @param {HtmlEditor} this
14148              */
14149             activate: true,
14150              /**
14151              * @event beforesync
14152              * Fires before the textarea is updated with content from the editor iframe. Return false
14153              * to cancel the sync.
14154              * @param {HtmlEditor} this
14155              * @param {String} html
14156              */
14157             beforesync: true,
14158              /**
14159              * @event beforepush
14160              * Fires before the iframe editor is updated with content from the textarea. Return false
14161              * to cancel the push.
14162              * @param {HtmlEditor} this
14163              * @param {String} html
14164              */
14165             beforepush: true,
14166              /**
14167              * @event sync
14168              * Fires when the textarea is updated with content from the editor iframe.
14169              * @param {HtmlEditor} this
14170              * @param {String} html
14171              */
14172             sync: true,
14173              /**
14174              * @event push
14175              * Fires when the iframe editor is updated with content from the textarea.
14176              * @param {HtmlEditor} this
14177              * @param {String} html
14178              */
14179             push: true,
14180              /**
14181              * @event editmodechange
14182              * Fires when the editor switches edit modes
14183              * @param {HtmlEditor} this
14184              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14185              */
14186             editmodechange: true,
14187             /**
14188              * @event editorevent
14189              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14190              * @param {HtmlEditor} this
14191              */
14192             editorevent: true,
14193             /**
14194              * @event firstfocus
14195              * Fires when on first focus - needed by toolbars..
14196              * @param {HtmlEditor} this
14197              */
14198             firstfocus: true,
14199             /**
14200              * @event autosave
14201              * Auto save the htmlEditor value as a file into Events
14202              * @param {HtmlEditor} this
14203              */
14204             autosave: true,
14205             /**
14206              * @event savedpreview
14207              * preview the saved version of htmlEditor
14208              * @param {HtmlEditor} this
14209              */
14210             savedpreview: true
14211         });
14212 };
14213
14214
14215 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14216     
14217     
14218       /**
14219      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14220      */
14221     toolbars : false,
14222    
14223      /**
14224      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14225      *                        Roo.resizable.
14226      */
14227     resizable : false,
14228      /**
14229      * @cfg {Number} height (in pixels)
14230      */   
14231     height: 300,
14232    /**
14233      * @cfg {Number} width (in pixels)
14234      */   
14235     width: false,
14236     
14237     /**
14238      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14239      * 
14240      */
14241     stylesheets: false,
14242     
14243     // id of frame..
14244     frameId: false,
14245     
14246     // private properties
14247     validationEvent : false,
14248     deferHeight: true,
14249     initialized : false,
14250     activated : false,
14251     
14252     onFocus : Roo.emptyFn,
14253     iframePad:3,
14254     hideMode:'offsets',
14255     
14256     
14257     tbContainer : false,
14258     
14259     toolbarContainer :function() {
14260         return this.wrap.select('.x-html-editor-tb',true).first();
14261     },
14262
14263     /**
14264      * Protected method that will not generally be called directly. It
14265      * is called when the editor creates its toolbar. Override this method if you need to
14266      * add custom toolbar buttons.
14267      * @param {HtmlEditor} editor
14268      */
14269     createToolbar : function(){
14270         
14271         Roo.log("create toolbars");
14272         
14273         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14274         this.toolbars[0].render(this.toolbarContainer());
14275         
14276         return;
14277         
14278 //        if (!editor.toolbars || !editor.toolbars.length) {
14279 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14280 //        }
14281 //        
14282 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14283 //            editor.toolbars[i] = Roo.factory(
14284 //                    typeof(editor.toolbars[i]) == 'string' ?
14285 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14286 //                Roo.bootstrap.HtmlEditor);
14287 //            editor.toolbars[i].init(editor);
14288 //        }
14289     },
14290
14291      
14292     // private
14293     onRender : function(ct, position)
14294     {
14295        // Roo.log("Call onRender: " + this.xtype);
14296         var _t = this;
14297         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14298       
14299         this.wrap = this.inputEl().wrap({
14300             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14301         });
14302         
14303         this.editorcore.onRender(ct, position);
14304          
14305         if (this.resizable) {
14306             this.resizeEl = new Roo.Resizable(this.wrap, {
14307                 pinned : true,
14308                 wrap: true,
14309                 dynamic : true,
14310                 minHeight : this.height,
14311                 height: this.height,
14312                 handles : this.resizable,
14313                 width: this.width,
14314                 listeners : {
14315                     resize : function(r, w, h) {
14316                         _t.onResize(w,h); // -something
14317                     }
14318                 }
14319             });
14320             
14321         }
14322         this.createToolbar(this);
14323        
14324         
14325         if(!this.width && this.resizable){
14326             this.setSize(this.wrap.getSize());
14327         }
14328         if (this.resizeEl) {
14329             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14330             // should trigger onReize..
14331         }
14332         
14333     },
14334
14335     // private
14336     onResize : function(w, h)
14337     {
14338         Roo.log('resize: ' +w + ',' + h );
14339         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14340         var ew = false;
14341         var eh = false;
14342         
14343         if(this.inputEl() ){
14344             if(typeof w == 'number'){
14345                 var aw = w - this.wrap.getFrameWidth('lr');
14346                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14347                 ew = aw;
14348             }
14349             if(typeof h == 'number'){
14350                  var tbh = -11;  // fixme it needs to tool bar size!
14351                 for (var i =0; i < this.toolbars.length;i++) {
14352                     // fixme - ask toolbars for heights?
14353                     tbh += this.toolbars[i].el.getHeight();
14354                     //if (this.toolbars[i].footer) {
14355                     //    tbh += this.toolbars[i].footer.el.getHeight();
14356                     //}
14357                 }
14358               
14359                 
14360                 
14361                 
14362                 
14363                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14364                 ah -= 5; // knock a few pixes off for look..
14365                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14366                 var eh = ah;
14367             }
14368         }
14369         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14370         this.editorcore.onResize(ew,eh);
14371         
14372     },
14373
14374     /**
14375      * Toggles the editor between standard and source edit mode.
14376      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14377      */
14378     toggleSourceEdit : function(sourceEditMode)
14379     {
14380         this.editorcore.toggleSourceEdit(sourceEditMode);
14381         
14382         if(this.editorcore.sourceEditMode){
14383             Roo.log('editor - showing textarea');
14384             
14385 //            Roo.log('in');
14386 //            Roo.log(this.syncValue());
14387             this.syncValue();
14388             this.inputEl().removeClass('hide');
14389             this.inputEl().dom.removeAttribute('tabIndex');
14390             this.inputEl().focus();
14391         }else{
14392             Roo.log('editor - hiding textarea');
14393 //            Roo.log('out')
14394 //            Roo.log(this.pushValue()); 
14395             this.pushValue();
14396             
14397             this.inputEl().addClass('hide');
14398             this.inputEl().dom.setAttribute('tabIndex', -1);
14399             //this.deferFocus();
14400         }
14401          
14402         if(this.resizable){
14403             this.setSize(this.wrap.getSize());
14404         }
14405         
14406         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14407     },
14408  
14409     // private (for BoxComponent)
14410     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14411
14412     // private (for BoxComponent)
14413     getResizeEl : function(){
14414         return this.wrap;
14415     },
14416
14417     // private (for BoxComponent)
14418     getPositionEl : function(){
14419         return this.wrap;
14420     },
14421
14422     // private
14423     initEvents : function(){
14424         this.originalValue = this.getValue();
14425     },
14426
14427 //    /**
14428 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14429 //     * @method
14430 //     */
14431 //    markInvalid : Roo.emptyFn,
14432 //    /**
14433 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14434 //     * @method
14435 //     */
14436 //    clearInvalid : Roo.emptyFn,
14437
14438     setValue : function(v){
14439         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14440         this.editorcore.pushValue();
14441     },
14442
14443      
14444     // private
14445     deferFocus : function(){
14446         this.focus.defer(10, this);
14447     },
14448
14449     // doc'ed in Field
14450     focus : function(){
14451         this.editorcore.focus();
14452         
14453     },
14454       
14455
14456     // private
14457     onDestroy : function(){
14458         
14459         
14460         
14461         if(this.rendered){
14462             
14463             for (var i =0; i < this.toolbars.length;i++) {
14464                 // fixme - ask toolbars for heights?
14465                 this.toolbars[i].onDestroy();
14466             }
14467             
14468             this.wrap.dom.innerHTML = '';
14469             this.wrap.remove();
14470         }
14471     },
14472
14473     // private
14474     onFirstFocus : function(){
14475         //Roo.log("onFirstFocus");
14476         this.editorcore.onFirstFocus();
14477          for (var i =0; i < this.toolbars.length;i++) {
14478             this.toolbars[i].onFirstFocus();
14479         }
14480         
14481     },
14482     
14483     // private
14484     syncValue : function()
14485     {   
14486         this.editorcore.syncValue();
14487     },
14488     
14489     pushValue : function()
14490     {   
14491         this.editorcore.pushValue();
14492     }
14493      
14494     
14495     // hide stuff that is not compatible
14496     /**
14497      * @event blur
14498      * @hide
14499      */
14500     /**
14501      * @event change
14502      * @hide
14503      */
14504     /**
14505      * @event focus
14506      * @hide
14507      */
14508     /**
14509      * @event specialkey
14510      * @hide
14511      */
14512     /**
14513      * @cfg {String} fieldClass @hide
14514      */
14515     /**
14516      * @cfg {String} focusClass @hide
14517      */
14518     /**
14519      * @cfg {String} autoCreate @hide
14520      */
14521     /**
14522      * @cfg {String} inputType @hide
14523      */
14524     /**
14525      * @cfg {String} invalidClass @hide
14526      */
14527     /**
14528      * @cfg {String} invalidText @hide
14529      */
14530     /**
14531      * @cfg {String} msgFx @hide
14532      */
14533     /**
14534      * @cfg {String} validateOnBlur @hide
14535      */
14536 });
14537  
14538     
14539    
14540    
14541    
14542       
14543
14544 /**
14545  * @class Roo.bootstrap.HtmlEditorToolbar1
14546  * Basic Toolbar
14547  * 
14548  * Usage:
14549  *
14550  new Roo.bootstrap.HtmlEditor({
14551     ....
14552     toolbars : [
14553         new Roo.bootstrap.HtmlEditorToolbar1({
14554             disable : { fonts: 1 , format: 1, ..., ... , ...],
14555             btns : [ .... ]
14556         })
14557     }
14558      
14559  * 
14560  * @cfg {Object} disable List of elements to disable..
14561  * @cfg {Array} btns List of additional buttons.
14562  * 
14563  * 
14564  * NEEDS Extra CSS? 
14565  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14566  */
14567  
14568 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14569 {
14570     
14571     Roo.apply(this, config);
14572     
14573     // default disabled, based on 'good practice'..
14574     this.disable = this.disable || {};
14575     Roo.applyIf(this.disable, {
14576         fontSize : true,
14577         colors : true,
14578         specialElements : true
14579     });
14580     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14581     
14582     this.editor = config.editor;
14583     this.editorcore = config.editor.editorcore;
14584     
14585     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14586     
14587     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14588     // dont call parent... till later.
14589 }
14590 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14591     
14592     
14593     bar : true,
14594     
14595     editor : false,
14596     editorcore : false,
14597     
14598     
14599     formats : [
14600         "p" ,  
14601         "h1","h2","h3","h4","h5","h6", 
14602         "pre", "code", 
14603         "abbr", "acronym", "address", "cite", "samp", "var",
14604         'div','span'
14605     ],
14606     
14607     onRender : function(ct, position)
14608     {
14609        // Roo.log("Call onRender: " + this.xtype);
14610         
14611        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14612        Roo.log(this.el);
14613        this.el.dom.style.marginBottom = '0';
14614        var _this = this;
14615        var editorcore = this.editorcore;
14616        var editor= this.editor;
14617        
14618        var children = [];
14619        var btn = function(id,cmd , toggle, handler){
14620        
14621             var  event = toggle ? 'toggle' : 'click';
14622        
14623             var a = {
14624                 size : 'sm',
14625                 xtype: 'Button',
14626                 xns: Roo.bootstrap,
14627                 glyphicon : id,
14628                 cmd : id || cmd,
14629                 enableToggle:toggle !== false,
14630                 //html : 'submit'
14631                 pressed : toggle ? false : null,
14632                 listeners : {}
14633             }
14634             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14635                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14636             }
14637             children.push(a);
14638             return a;
14639        }
14640         
14641         var style = {
14642                 xtype: 'Button',
14643                 size : 'sm',
14644                 xns: Roo.bootstrap,
14645                 glyphicon : 'font',
14646                 //html : 'submit'
14647                 menu : {
14648                     xtype: 'Menu',
14649                     xns: Roo.bootstrap,
14650                     items:  []
14651                 }
14652         };
14653         Roo.each(this.formats, function(f) {
14654             style.menu.items.push({
14655                 xtype :'MenuItem',
14656                 xns: Roo.bootstrap,
14657                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14658                 tagname : f,
14659                 listeners : {
14660                     click : function()
14661                     {
14662                         editorcore.insertTag(this.tagname);
14663                         editor.focus();
14664                     }
14665                 }
14666                 
14667             });
14668         });
14669          children.push(style);   
14670             
14671             
14672         btn('bold',false,true);
14673         btn('italic',false,true);
14674         btn('align-left', 'justifyleft',true);
14675         btn('align-center', 'justifycenter',true);
14676         btn('align-right' , 'justifyright',true);
14677         btn('link', false, false, function(btn) {
14678             //Roo.log("create link?");
14679             var url = prompt(this.createLinkText, this.defaultLinkValue);
14680             if(url && url != 'http:/'+'/'){
14681                 this.editorcore.relayCmd('createlink', url);
14682             }
14683         }),
14684         btn('list','insertunorderedlist',true);
14685         btn('pencil', false,true, function(btn){
14686                 Roo.log(this);
14687                 
14688                 this.toggleSourceEdit(btn.pressed);
14689         });
14690         /*
14691         var cog = {
14692                 xtype: 'Button',
14693                 size : 'sm',
14694                 xns: Roo.bootstrap,
14695                 glyphicon : 'cog',
14696                 //html : 'submit'
14697                 menu : {
14698                     xtype: 'Menu',
14699                     xns: Roo.bootstrap,
14700                     items:  []
14701                 }
14702         };
14703         
14704         cog.menu.items.push({
14705             xtype :'MenuItem',
14706             xns: Roo.bootstrap,
14707             html : Clean styles,
14708             tagname : f,
14709             listeners : {
14710                 click : function()
14711                 {
14712                     editorcore.insertTag(this.tagname);
14713                     editor.focus();
14714                 }
14715             }
14716             
14717         });
14718        */
14719         
14720          
14721        this.xtype = 'Navbar';
14722         
14723         for(var i=0;i< children.length;i++) {
14724             
14725             this.buttons.add(this.addxtypeChild(children[i]));
14726             
14727         }
14728         
14729         editor.on('editorevent', this.updateToolbar, this);
14730     },
14731     onBtnClick : function(id)
14732     {
14733        this.editorcore.relayCmd(id);
14734        this.editorcore.focus();
14735     },
14736     
14737     /**
14738      * Protected method that will not generally be called directly. It triggers
14739      * a toolbar update by reading the markup state of the current selection in the editor.
14740      */
14741     updateToolbar: function(){
14742
14743         if(!this.editorcore.activated){
14744             this.editor.onFirstFocus(); // is this neeed?
14745             return;
14746         }
14747
14748         var btns = this.buttons; 
14749         var doc = this.editorcore.doc;
14750         btns.get('bold').setActive(doc.queryCommandState('bold'));
14751         btns.get('italic').setActive(doc.queryCommandState('italic'));
14752         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14753         
14754         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14755         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14756         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14757         
14758         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14759         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14760          /*
14761         
14762         var ans = this.editorcore.getAllAncestors();
14763         if (this.formatCombo) {
14764             
14765             
14766             var store = this.formatCombo.store;
14767             this.formatCombo.setValue("");
14768             for (var i =0; i < ans.length;i++) {
14769                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14770                     // select it..
14771                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14772                     break;
14773                 }
14774             }
14775         }
14776         
14777         
14778         
14779         // hides menus... - so this cant be on a menu...
14780         Roo.bootstrap.MenuMgr.hideAll();
14781         */
14782         Roo.bootstrap.MenuMgr.hideAll();
14783         //this.editorsyncValue();
14784     },
14785     onFirstFocus: function() {
14786         this.buttons.each(function(item){
14787            item.enable();
14788         });
14789     },
14790     toggleSourceEdit : function(sourceEditMode){
14791         
14792           
14793         if(sourceEditMode){
14794             Roo.log("disabling buttons");
14795            this.buttons.each( function(item){
14796                 if(item.cmd != 'pencil'){
14797                     item.disable();
14798                 }
14799             });
14800           
14801         }else{
14802             Roo.log("enabling buttons");
14803             if(this.editorcore.initialized){
14804                 this.buttons.each( function(item){
14805                     item.enable();
14806                 });
14807             }
14808             
14809         }
14810         Roo.log("calling toggole on editor");
14811         // tell the editor that it's been pressed..
14812         this.editor.toggleSourceEdit(sourceEditMode);
14813        
14814     }
14815 });
14816
14817
14818
14819
14820
14821 /**
14822  * @class Roo.bootstrap.Table.AbstractSelectionModel
14823  * @extends Roo.util.Observable
14824  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14825  * implemented by descendant classes.  This class should not be directly instantiated.
14826  * @constructor
14827  */
14828 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14829     this.locked = false;
14830     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14831 };
14832
14833
14834 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14835     /** @ignore Called by the grid automatically. Do not call directly. */
14836     init : function(grid){
14837         this.grid = grid;
14838         this.initEvents();
14839     },
14840
14841     /**
14842      * Locks the selections.
14843      */
14844     lock : function(){
14845         this.locked = true;
14846     },
14847
14848     /**
14849      * Unlocks the selections.
14850      */
14851     unlock : function(){
14852         this.locked = false;
14853     },
14854
14855     /**
14856      * Returns true if the selections are locked.
14857      * @return {Boolean}
14858      */
14859     isLocked : function(){
14860         return this.locked;
14861     }
14862 });
14863 /**
14864  * @class Roo.bootstrap.Table.ColumnModel
14865  * @extends Roo.util.Observable
14866  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14867  * the columns in the table.
14868  
14869  * @constructor
14870  * @param {Object} config An Array of column config objects. See this class's
14871  * config objects for details.
14872 */
14873 Roo.bootstrap.Table.ColumnModel = function(config){
14874         /**
14875      * The config passed into the constructor
14876      */
14877     this.config = config;
14878     this.lookup = {};
14879
14880     // if no id, create one
14881     // if the column does not have a dataIndex mapping,
14882     // map it to the order it is in the config
14883     for(var i = 0, len = config.length; i < len; i++){
14884         var c = config[i];
14885         if(typeof c.dataIndex == "undefined"){
14886             c.dataIndex = i;
14887         }
14888         if(typeof c.renderer == "string"){
14889             c.renderer = Roo.util.Format[c.renderer];
14890         }
14891         if(typeof c.id == "undefined"){
14892             c.id = Roo.id();
14893         }
14894 //        if(c.editor && c.editor.xtype){
14895 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14896 //        }
14897 //        if(c.editor && c.editor.isFormField){
14898 //            c.editor = new Roo.grid.GridEditor(c.editor);
14899 //        }
14900
14901         this.lookup[c.id] = c;
14902     }
14903
14904     /**
14905      * The width of columns which have no width specified (defaults to 100)
14906      * @type Number
14907      */
14908     this.defaultWidth = 100;
14909
14910     /**
14911      * Default sortable of columns which have no sortable specified (defaults to false)
14912      * @type Boolean
14913      */
14914     this.defaultSortable = false;
14915
14916     this.addEvents({
14917         /**
14918              * @event widthchange
14919              * Fires when the width of a column changes.
14920              * @param {ColumnModel} this
14921              * @param {Number} columnIndex The column index
14922              * @param {Number} newWidth The new width
14923              */
14924             "widthchange": true,
14925         /**
14926              * @event headerchange
14927              * Fires when the text of a header changes.
14928              * @param {ColumnModel} this
14929              * @param {Number} columnIndex The column index
14930              * @param {Number} newText The new header text
14931              */
14932             "headerchange": true,
14933         /**
14934              * @event hiddenchange
14935              * Fires when a column is hidden or "unhidden".
14936              * @param {ColumnModel} this
14937              * @param {Number} columnIndex The column index
14938              * @param {Boolean} hidden true if hidden, false otherwise
14939              */
14940             "hiddenchange": true,
14941             /**
14942          * @event columnmoved
14943          * Fires when a column is moved.
14944          * @param {ColumnModel} this
14945          * @param {Number} oldIndex
14946          * @param {Number} newIndex
14947          */
14948         "columnmoved" : true,
14949         /**
14950          * @event columlockchange
14951          * Fires when a column's locked state is changed
14952          * @param {ColumnModel} this
14953          * @param {Number} colIndex
14954          * @param {Boolean} locked true if locked
14955          */
14956         "columnlockchange" : true
14957     });
14958     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14959 };
14960 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14961     /**
14962      * @cfg {String} header The header text to display in the Grid view.
14963      */
14964     /**
14965      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14966      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14967      * specified, the column's index is used as an index into the Record's data Array.
14968      */
14969     /**
14970      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14971      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14972      */
14973     /**
14974      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14975      * Defaults to the value of the {@link #defaultSortable} property.
14976      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14977      */
14978     /**
14979      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14980      */
14981     /**
14982      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14983      */
14984     /**
14985      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14986      */
14987     /**
14988      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14989      */
14990     /**
14991      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14992      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14993      * default renderer uses the raw data value.
14994      */
14995     /**
14996      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14997      */
14998
14999     /**
15000      * Returns the id of the column at the specified index.
15001      * @param {Number} index The column index
15002      * @return {String} the id
15003      */
15004     getColumnId : function(index){
15005         return this.config[index].id;
15006     },
15007
15008     /**
15009      * Returns the column for a specified id.
15010      * @param {String} id The column id
15011      * @return {Object} the column
15012      */
15013     getColumnById : function(id){
15014         return this.lookup[id];
15015     },
15016
15017     
15018     /**
15019      * Returns the column for a specified dataIndex.
15020      * @param {String} dataIndex The column dataIndex
15021      * @return {Object|Boolean} the column or false if not found
15022      */
15023     getColumnByDataIndex: function(dataIndex){
15024         var index = this.findColumnIndex(dataIndex);
15025         return index > -1 ? this.config[index] : false;
15026     },
15027     
15028     /**
15029      * Returns the index for a specified column id.
15030      * @param {String} id The column id
15031      * @return {Number} the index, or -1 if not found
15032      */
15033     getIndexById : function(id){
15034         for(var i = 0, len = this.config.length; i < len; i++){
15035             if(this.config[i].id == id){
15036                 return i;
15037             }
15038         }
15039         return -1;
15040     },
15041     
15042     /**
15043      * Returns the index for a specified column dataIndex.
15044      * @param {String} dataIndex The column dataIndex
15045      * @return {Number} the index, or -1 if not found
15046      */
15047     
15048     findColumnIndex : function(dataIndex){
15049         for(var i = 0, len = this.config.length; i < len; i++){
15050             if(this.config[i].dataIndex == dataIndex){
15051                 return i;
15052             }
15053         }
15054         return -1;
15055     },
15056     
15057     
15058     moveColumn : function(oldIndex, newIndex){
15059         var c = this.config[oldIndex];
15060         this.config.splice(oldIndex, 1);
15061         this.config.splice(newIndex, 0, c);
15062         this.dataMap = null;
15063         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15064     },
15065
15066     isLocked : function(colIndex){
15067         return this.config[colIndex].locked === true;
15068     },
15069
15070     setLocked : function(colIndex, value, suppressEvent){
15071         if(this.isLocked(colIndex) == value){
15072             return;
15073         }
15074         this.config[colIndex].locked = value;
15075         if(!suppressEvent){
15076             this.fireEvent("columnlockchange", this, colIndex, value);
15077         }
15078     },
15079
15080     getTotalLockedWidth : function(){
15081         var totalWidth = 0;
15082         for(var i = 0; i < this.config.length; i++){
15083             if(this.isLocked(i) && !this.isHidden(i)){
15084                 this.totalWidth += this.getColumnWidth(i);
15085             }
15086         }
15087         return totalWidth;
15088     },
15089
15090     getLockedCount : function(){
15091         for(var i = 0, len = this.config.length; i < len; i++){
15092             if(!this.isLocked(i)){
15093                 return i;
15094             }
15095         }
15096     },
15097
15098     /**
15099      * Returns the number of columns.
15100      * @return {Number}
15101      */
15102     getColumnCount : function(visibleOnly){
15103         if(visibleOnly === true){
15104             var c = 0;
15105             for(var i = 0, len = this.config.length; i < len; i++){
15106                 if(!this.isHidden(i)){
15107                     c++;
15108                 }
15109             }
15110             return c;
15111         }
15112         return this.config.length;
15113     },
15114
15115     /**
15116      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15117      * @param {Function} fn
15118      * @param {Object} scope (optional)
15119      * @return {Array} result
15120      */
15121     getColumnsBy : function(fn, scope){
15122         var r = [];
15123         for(var i = 0, len = this.config.length; i < len; i++){
15124             var c = this.config[i];
15125             if(fn.call(scope||this, c, i) === true){
15126                 r[r.length] = c;
15127             }
15128         }
15129         return r;
15130     },
15131
15132     /**
15133      * Returns true if the specified column is sortable.
15134      * @param {Number} col The column index
15135      * @return {Boolean}
15136      */
15137     isSortable : function(col){
15138         if(typeof this.config[col].sortable == "undefined"){
15139             return this.defaultSortable;
15140         }
15141         return this.config[col].sortable;
15142     },
15143
15144     /**
15145      * Returns the rendering (formatting) function defined for the column.
15146      * @param {Number} col The column index.
15147      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15148      */
15149     getRenderer : function(col){
15150         if(!this.config[col].renderer){
15151             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15152         }
15153         return this.config[col].renderer;
15154     },
15155
15156     /**
15157      * Sets the rendering (formatting) function for a column.
15158      * @param {Number} col The column index
15159      * @param {Function} fn The function to use to process the cell's raw data
15160      * to return HTML markup for the grid view. The render function is called with
15161      * the following parameters:<ul>
15162      * <li>Data value.</li>
15163      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15164      * <li>css A CSS style string to apply to the table cell.</li>
15165      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15166      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15167      * <li>Row index</li>
15168      * <li>Column index</li>
15169      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15170      */
15171     setRenderer : function(col, fn){
15172         this.config[col].renderer = fn;
15173     },
15174
15175     /**
15176      * Returns the width for the specified column.
15177      * @param {Number} col The column index
15178      * @return {Number}
15179      */
15180     getColumnWidth : function(col){
15181         return this.config[col].width * 1 || this.defaultWidth;
15182     },
15183
15184     /**
15185      * Sets the width for a column.
15186      * @param {Number} col The column index
15187      * @param {Number} width The new width
15188      */
15189     setColumnWidth : function(col, width, suppressEvent){
15190         this.config[col].width = width;
15191         this.totalWidth = null;
15192         if(!suppressEvent){
15193              this.fireEvent("widthchange", this, col, width);
15194         }
15195     },
15196
15197     /**
15198      * Returns the total width of all columns.
15199      * @param {Boolean} includeHidden True to include hidden column widths
15200      * @return {Number}
15201      */
15202     getTotalWidth : function(includeHidden){
15203         if(!this.totalWidth){
15204             this.totalWidth = 0;
15205             for(var i = 0, len = this.config.length; i < len; i++){
15206                 if(includeHidden || !this.isHidden(i)){
15207                     this.totalWidth += this.getColumnWidth(i);
15208                 }
15209             }
15210         }
15211         return this.totalWidth;
15212     },
15213
15214     /**
15215      * Returns the header for the specified column.
15216      * @param {Number} col The column index
15217      * @return {String}
15218      */
15219     getColumnHeader : function(col){
15220         return this.config[col].header;
15221     },
15222
15223     /**
15224      * Sets the header for a column.
15225      * @param {Number} col The column index
15226      * @param {String} header The new header
15227      */
15228     setColumnHeader : function(col, header){
15229         this.config[col].header = header;
15230         this.fireEvent("headerchange", this, col, header);
15231     },
15232
15233     /**
15234      * Returns the tooltip for the specified column.
15235      * @param {Number} col The column index
15236      * @return {String}
15237      */
15238     getColumnTooltip : function(col){
15239             return this.config[col].tooltip;
15240     },
15241     /**
15242      * Sets the tooltip for a column.
15243      * @param {Number} col The column index
15244      * @param {String} tooltip The new tooltip
15245      */
15246     setColumnTooltip : function(col, tooltip){
15247             this.config[col].tooltip = tooltip;
15248     },
15249
15250     /**
15251      * Returns the dataIndex for the specified column.
15252      * @param {Number} col The column index
15253      * @return {Number}
15254      */
15255     getDataIndex : function(col){
15256         return this.config[col].dataIndex;
15257     },
15258
15259     /**
15260      * Sets the dataIndex for a column.
15261      * @param {Number} col The column index
15262      * @param {Number} dataIndex The new dataIndex
15263      */
15264     setDataIndex : function(col, dataIndex){
15265         this.config[col].dataIndex = dataIndex;
15266     },
15267
15268     
15269     
15270     /**
15271      * Returns true if the cell is editable.
15272      * @param {Number} colIndex The column index
15273      * @param {Number} rowIndex The row index
15274      * @return {Boolean}
15275      */
15276     isCellEditable : function(colIndex, rowIndex){
15277         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15278     },
15279
15280     /**
15281      * Returns the editor defined for the cell/column.
15282      * return false or null to disable editing.
15283      * @param {Number} colIndex The column index
15284      * @param {Number} rowIndex The row index
15285      * @return {Object}
15286      */
15287     getCellEditor : function(colIndex, rowIndex){
15288         return this.config[colIndex].editor;
15289     },
15290
15291     /**
15292      * Sets if a column is editable.
15293      * @param {Number} col The column index
15294      * @param {Boolean} editable True if the column is editable
15295      */
15296     setEditable : function(col, editable){
15297         this.config[col].editable = editable;
15298     },
15299
15300
15301     /**
15302      * Returns true if the column is hidden.
15303      * @param {Number} colIndex The column index
15304      * @return {Boolean}
15305      */
15306     isHidden : function(colIndex){
15307         return this.config[colIndex].hidden;
15308     },
15309
15310
15311     /**
15312      * Returns true if the column width cannot be changed
15313      */
15314     isFixed : function(colIndex){
15315         return this.config[colIndex].fixed;
15316     },
15317
15318     /**
15319      * Returns true if the column can be resized
15320      * @return {Boolean}
15321      */
15322     isResizable : function(colIndex){
15323         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15324     },
15325     /**
15326      * Sets if a column is hidden.
15327      * @param {Number} colIndex The column index
15328      * @param {Boolean} hidden True if the column is hidden
15329      */
15330     setHidden : function(colIndex, hidden){
15331         this.config[colIndex].hidden = hidden;
15332         this.totalWidth = null;
15333         this.fireEvent("hiddenchange", this, colIndex, hidden);
15334     },
15335
15336     /**
15337      * Sets the editor for a column.
15338      * @param {Number} col The column index
15339      * @param {Object} editor The editor object
15340      */
15341     setEditor : function(col, editor){
15342         this.config[col].editor = editor;
15343     }
15344 });
15345
15346 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15347         if(typeof value == "string" && value.length < 1){
15348             return "&#160;";
15349         }
15350         return value;
15351 };
15352
15353 // Alias for backwards compatibility
15354 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15355
15356 /**
15357  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15358  * @class Roo.bootstrap.Table.RowSelectionModel
15359  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15360  * It supports multiple selections and keyboard selection/navigation. 
15361  * @constructor
15362  * @param {Object} config
15363  */
15364
15365 Roo.bootstrap.Table.RowSelectionModel = function(config){
15366     Roo.apply(this, config);
15367     this.selections = new Roo.util.MixedCollection(false, function(o){
15368         return o.id;
15369     });
15370
15371     this.last = false;
15372     this.lastActive = false;
15373
15374     this.addEvents({
15375         /**
15376              * @event selectionchange
15377              * Fires when the selection changes
15378              * @param {SelectionModel} this
15379              */
15380             "selectionchange" : true,
15381         /**
15382              * @event afterselectionchange
15383              * Fires after the selection changes (eg. by key press or clicking)
15384              * @param {SelectionModel} this
15385              */
15386             "afterselectionchange" : true,
15387         /**
15388              * @event beforerowselect
15389              * Fires when a row is selected being selected, return false to cancel.
15390              * @param {SelectionModel} this
15391              * @param {Number} rowIndex The selected index
15392              * @param {Boolean} keepExisting False if other selections will be cleared
15393              */
15394             "beforerowselect" : true,
15395         /**
15396              * @event rowselect
15397              * Fires when a row is selected.
15398              * @param {SelectionModel} this
15399              * @param {Number} rowIndex The selected index
15400              * @param {Roo.data.Record} r The record
15401              */
15402             "rowselect" : true,
15403         /**
15404              * @event rowdeselect
15405              * Fires when a row is deselected.
15406              * @param {SelectionModel} this
15407              * @param {Number} rowIndex The selected index
15408              */
15409         "rowdeselect" : true
15410     });
15411     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15412     this.locked = false;
15413 };
15414
15415 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15416     /**
15417      * @cfg {Boolean} singleSelect
15418      * True to allow selection of only one row at a time (defaults to false)
15419      */
15420     singleSelect : false,
15421
15422     // private
15423     initEvents : function(){
15424
15425         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15426             this.grid.on("mousedown", this.handleMouseDown, this);
15427         }else{ // allow click to work like normal
15428             this.grid.on("rowclick", this.handleDragableRowClick, this);
15429         }
15430
15431         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15432             "up" : function(e){
15433                 if(!e.shiftKey){
15434                     this.selectPrevious(e.shiftKey);
15435                 }else if(this.last !== false && this.lastActive !== false){
15436                     var last = this.last;
15437                     this.selectRange(this.last,  this.lastActive-1);
15438                     this.grid.getView().focusRow(this.lastActive);
15439                     if(last !== false){
15440                         this.last = last;
15441                     }
15442                 }else{
15443                     this.selectFirstRow();
15444                 }
15445                 this.fireEvent("afterselectionchange", this);
15446             },
15447             "down" : function(e){
15448                 if(!e.shiftKey){
15449                     this.selectNext(e.shiftKey);
15450                 }else if(this.last !== false && this.lastActive !== false){
15451                     var last = this.last;
15452                     this.selectRange(this.last,  this.lastActive+1);
15453                     this.grid.getView().focusRow(this.lastActive);
15454                     if(last !== false){
15455                         this.last = last;
15456                     }
15457                 }else{
15458                     this.selectFirstRow();
15459                 }
15460                 this.fireEvent("afterselectionchange", this);
15461             },
15462             scope: this
15463         });
15464
15465         var view = this.grid.view;
15466         view.on("refresh", this.onRefresh, this);
15467         view.on("rowupdated", this.onRowUpdated, this);
15468         view.on("rowremoved", this.onRemove, this);
15469     },
15470
15471     // private
15472     onRefresh : function(){
15473         var ds = this.grid.dataSource, i, v = this.grid.view;
15474         var s = this.selections;
15475         s.each(function(r){
15476             if((i = ds.indexOfId(r.id)) != -1){
15477                 v.onRowSelect(i);
15478             }else{
15479                 s.remove(r);
15480             }
15481         });
15482     },
15483
15484     // private
15485     onRemove : function(v, index, r){
15486         this.selections.remove(r);
15487     },
15488
15489     // private
15490     onRowUpdated : function(v, index, r){
15491         if(this.isSelected(r)){
15492             v.onRowSelect(index);
15493         }
15494     },
15495
15496     /**
15497      * Select records.
15498      * @param {Array} records The records to select
15499      * @param {Boolean} keepExisting (optional) True to keep existing selections
15500      */
15501     selectRecords : function(records, keepExisting){
15502         if(!keepExisting){
15503             this.clearSelections();
15504         }
15505         var ds = this.grid.dataSource;
15506         for(var i = 0, len = records.length; i < len; i++){
15507             this.selectRow(ds.indexOf(records[i]), true);
15508         }
15509     },
15510
15511     /**
15512      * Gets the number of selected rows.
15513      * @return {Number}
15514      */
15515     getCount : function(){
15516         return this.selections.length;
15517     },
15518
15519     /**
15520      * Selects the first row in the grid.
15521      */
15522     selectFirstRow : function(){
15523         this.selectRow(0);
15524     },
15525
15526     /**
15527      * Select the last row.
15528      * @param {Boolean} keepExisting (optional) True to keep existing selections
15529      */
15530     selectLastRow : function(keepExisting){
15531         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15532     },
15533
15534     /**
15535      * Selects the row immediately following the last selected row.
15536      * @param {Boolean} keepExisting (optional) True to keep existing selections
15537      */
15538     selectNext : function(keepExisting){
15539         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15540             this.selectRow(this.last+1, keepExisting);
15541             this.grid.getView().focusRow(this.last);
15542         }
15543     },
15544
15545     /**
15546      * Selects the row that precedes the last selected row.
15547      * @param {Boolean} keepExisting (optional) True to keep existing selections
15548      */
15549     selectPrevious : function(keepExisting){
15550         if(this.last){
15551             this.selectRow(this.last-1, keepExisting);
15552             this.grid.getView().focusRow(this.last);
15553         }
15554     },
15555
15556     /**
15557      * Returns the selected records
15558      * @return {Array} Array of selected records
15559      */
15560     getSelections : function(){
15561         return [].concat(this.selections.items);
15562     },
15563
15564     /**
15565      * Returns the first selected record.
15566      * @return {Record}
15567      */
15568     getSelected : function(){
15569         return this.selections.itemAt(0);
15570     },
15571
15572
15573     /**
15574      * Clears all selections.
15575      */
15576     clearSelections : function(fast){
15577         if(this.locked) return;
15578         if(fast !== true){
15579             var ds = this.grid.dataSource;
15580             var s = this.selections;
15581             s.each(function(r){
15582                 this.deselectRow(ds.indexOfId(r.id));
15583             }, this);
15584             s.clear();
15585         }else{
15586             this.selections.clear();
15587         }
15588         this.last = false;
15589     },
15590
15591
15592     /**
15593      * Selects all rows.
15594      */
15595     selectAll : function(){
15596         if(this.locked) return;
15597         this.selections.clear();
15598         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15599             this.selectRow(i, true);
15600         }
15601     },
15602
15603     /**
15604      * Returns True if there is a selection.
15605      * @return {Boolean}
15606      */
15607     hasSelection : function(){
15608         return this.selections.length > 0;
15609     },
15610
15611     /**
15612      * Returns True if the specified row is selected.
15613      * @param {Number/Record} record The record or index of the record to check
15614      * @return {Boolean}
15615      */
15616     isSelected : function(index){
15617         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15618         return (r && this.selections.key(r.id) ? true : false);
15619     },
15620
15621     /**
15622      * Returns True if the specified record id is selected.
15623      * @param {String} id The id of record to check
15624      * @return {Boolean}
15625      */
15626     isIdSelected : function(id){
15627         return (this.selections.key(id) ? true : false);
15628     },
15629
15630     // private
15631     handleMouseDown : function(e, t){
15632         var view = this.grid.getView(), rowIndex;
15633         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15634             return;
15635         };
15636         if(e.shiftKey && this.last !== false){
15637             var last = this.last;
15638             this.selectRange(last, rowIndex, e.ctrlKey);
15639             this.last = last; // reset the last
15640             view.focusRow(rowIndex);
15641         }else{
15642             var isSelected = this.isSelected(rowIndex);
15643             if(e.button !== 0 && isSelected){
15644                 view.focusRow(rowIndex);
15645             }else if(e.ctrlKey && isSelected){
15646                 this.deselectRow(rowIndex);
15647             }else if(!isSelected){
15648                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15649                 view.focusRow(rowIndex);
15650             }
15651         }
15652         this.fireEvent("afterselectionchange", this);
15653     },
15654     // private
15655     handleDragableRowClick :  function(grid, rowIndex, e) 
15656     {
15657         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15658             this.selectRow(rowIndex, false);
15659             grid.view.focusRow(rowIndex);
15660              this.fireEvent("afterselectionchange", this);
15661         }
15662     },
15663     
15664     /**
15665      * Selects multiple rows.
15666      * @param {Array} rows Array of the indexes of the row to select
15667      * @param {Boolean} keepExisting (optional) True to keep existing selections
15668      */
15669     selectRows : function(rows, keepExisting){
15670         if(!keepExisting){
15671             this.clearSelections();
15672         }
15673         for(var i = 0, len = rows.length; i < len; i++){
15674             this.selectRow(rows[i], true);
15675         }
15676     },
15677
15678     /**
15679      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15680      * @param {Number} startRow The index of the first row in the range
15681      * @param {Number} endRow The index of the last row in the range
15682      * @param {Boolean} keepExisting (optional) True to retain existing selections
15683      */
15684     selectRange : function(startRow, endRow, keepExisting){
15685         if(this.locked) return;
15686         if(!keepExisting){
15687             this.clearSelections();
15688         }
15689         if(startRow <= endRow){
15690             for(var i = startRow; i <= endRow; i++){
15691                 this.selectRow(i, true);
15692             }
15693         }else{
15694             for(var i = startRow; i >= endRow; i--){
15695                 this.selectRow(i, true);
15696             }
15697         }
15698     },
15699
15700     /**
15701      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15702      * @param {Number} startRow The index of the first row in the range
15703      * @param {Number} endRow The index of the last row in the range
15704      */
15705     deselectRange : function(startRow, endRow, preventViewNotify){
15706         if(this.locked) return;
15707         for(var i = startRow; i <= endRow; i++){
15708             this.deselectRow(i, preventViewNotify);
15709         }
15710     },
15711
15712     /**
15713      * Selects a row.
15714      * @param {Number} row The index of the row to select
15715      * @param {Boolean} keepExisting (optional) True to keep existing selections
15716      */
15717     selectRow : function(index, keepExisting, preventViewNotify){
15718         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15719         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15720             if(!keepExisting || this.singleSelect){
15721                 this.clearSelections();
15722             }
15723             var r = this.grid.dataSource.getAt(index);
15724             this.selections.add(r);
15725             this.last = this.lastActive = index;
15726             if(!preventViewNotify){
15727                 this.grid.getView().onRowSelect(index);
15728             }
15729             this.fireEvent("rowselect", this, index, r);
15730             this.fireEvent("selectionchange", this);
15731         }
15732     },
15733
15734     /**
15735      * Deselects a row.
15736      * @param {Number} row The index of the row to deselect
15737      */
15738     deselectRow : function(index, preventViewNotify){
15739         if(this.locked) return;
15740         if(this.last == index){
15741             this.last = false;
15742         }
15743         if(this.lastActive == index){
15744             this.lastActive = false;
15745         }
15746         var r = this.grid.dataSource.getAt(index);
15747         this.selections.remove(r);
15748         if(!preventViewNotify){
15749             this.grid.getView().onRowDeselect(index);
15750         }
15751         this.fireEvent("rowdeselect", this, index);
15752         this.fireEvent("selectionchange", this);
15753     },
15754
15755     // private
15756     restoreLast : function(){
15757         if(this._last){
15758             this.last = this._last;
15759         }
15760     },
15761
15762     // private
15763     acceptsNav : function(row, col, cm){
15764         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15765     },
15766
15767     // private
15768     onEditorKey : function(field, e){
15769         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15770         if(k == e.TAB){
15771             e.stopEvent();
15772             ed.completeEdit();
15773             if(e.shiftKey){
15774                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15775             }else{
15776                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15777             }
15778         }else if(k == e.ENTER && !e.ctrlKey){
15779             e.stopEvent();
15780             ed.completeEdit();
15781             if(e.shiftKey){
15782                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15783             }else{
15784                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15785             }
15786         }else if(k == e.ESC){
15787             ed.cancelEdit();
15788         }
15789         if(newCell){
15790             g.startEditing(newCell[0], newCell[1]);
15791         }
15792     }
15793 });/*
15794  * - LGPL
15795  *
15796  * element
15797  * 
15798  */
15799
15800 /**
15801  * @class Roo.bootstrap.MessageBar
15802  * @extends Roo.bootstrap.Component
15803  * Bootstrap MessageBar class
15804  * @cfg {String} html contents of the MessageBar
15805  * @cfg {String} weight (info | success | warning | danger) default info
15806  * @cfg {String} beforeClass insert the bar before the given class
15807  * @cfg {Boolean} closable (true | false) default false
15808  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15809  * 
15810  * @constructor
15811  * Create a new Element
15812  * @param {Object} config The config object
15813  */
15814
15815 Roo.bootstrap.MessageBar = function(config){
15816     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15817 };
15818
15819 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15820     
15821     html: '',
15822     weight: 'info',
15823     closable: false,
15824     fixed: false,
15825     beforeClass: 'bootstrap-sticky-wrap',
15826     
15827     getAutoCreate : function(){
15828         
15829         var cfg = {
15830             tag: 'div',
15831             cls: 'alert alert-dismissable alert-' + this.weight,
15832             cn: [
15833                 {
15834                     tag: 'span',
15835                     cls: 'message',
15836                     html: this.html || ''
15837                 }
15838             ]
15839         }
15840         
15841         if(this.fixed){
15842             cfg.cls += ' alert-messages-fixed';
15843         }
15844         
15845         if(this.closable){
15846             cfg.cn.push({
15847                 tag: 'button',
15848                 cls: 'close',
15849                 html: 'x'
15850             });
15851         }
15852         
15853         return cfg;
15854     },
15855     
15856     onRender : function(ct, position)
15857     {
15858         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15859         
15860         if(!this.el){
15861             var cfg = Roo.apply({},  this.getAutoCreate());
15862             cfg.id = Roo.id();
15863             
15864             if (this.cls) {
15865                 cfg.cls += ' ' + this.cls;
15866             }
15867             if (this.style) {
15868                 cfg.style = this.style;
15869             }
15870             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15871             
15872             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15873         }
15874         
15875         this.el.select('>button.close').on('click', this.hide, this);
15876         
15877     },
15878     
15879     show : function()
15880     {
15881         if (!this.rendered) {
15882             this.render();
15883         }
15884         
15885         this.el.show();
15886         
15887         this.fireEvent('show', this);
15888         
15889     },
15890     
15891     hide : function()
15892     {
15893         if (!this.rendered) {
15894             this.render();
15895         }
15896         
15897         this.el.hide();
15898         
15899         this.fireEvent('hide', this);
15900     },
15901     
15902     update : function()
15903     {
15904 //        var e = this.el.dom.firstChild;
15905 //        
15906 //        if(this.closable){
15907 //            e = e.nextSibling;
15908 //        }
15909 //        
15910 //        e.data = this.html || '';
15911
15912         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15913     }
15914    
15915 });
15916
15917  
15918
15919