Roo/bootstrap/Table.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position){
306         
307         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472     
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = this.html || cfg.html;
489         
490         if (this.toggle == true) {
491             cfg={
492                 tag: 'div',
493                 cls: 'slider-frame roo-button',
494                 cn: [
495                     {
496                         tag: 'span',
497                         'data-on-text':'ON',
498                         'data-off-text':'OFF',
499                         cls: 'slider-button',
500                         html: this.offtext
501                     }
502                 ]
503             };
504             
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 cfg.cls += ' '+this.weight;
507             }
508             
509             return cfg;
510         }
511         
512         if (this.isClose) {
513             cfg.cls += ' close';
514             
515             cfg["aria-hidden"] = true;
516             
517             cfg.html = "&times;";
518             
519             return cfg;
520         }
521         
522          
523         if (this.theme==='default') {
524             cfg.cls = 'btn roo-button';
525             
526             //if (this.parentType != 'Navbar') {
527             this.weight = this.weight.length ?  this.weight : 'default';
528             //}
529             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
530                 
531                 cfg.cls += ' btn-' + this.weight;
532             }
533         } else if (this.theme==='glow') {
534             
535             cfg.tag = 'a';
536             cfg.cls = 'btn-glow roo-button';
537             
538             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
539                 
540                 cfg.cls += ' ' + this.weight;
541             }
542         }
543    
544         
545         if (this.inverse) {
546             this.cls += ' inverse';
547         }
548         
549         
550         if (this.active) {
551             cfg.cls += ' active';
552         }
553         
554         if (this.disabled) {
555             cfg.disabled = 'disabled';
556         }
557         
558         if (this.items) {
559             Roo.log('changing to ul' );
560             cfg.tag = 'ul';
561             this.glyphicon = 'caret';
562         }
563         
564         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
565          
566         //gsRoo.log(this.parentType);
567         if (this.parentType === 'Navbar' && !this.parent().bar) {
568             Roo.log('changing to li?');
569             
570             cfg.tag = 'li';
571             
572             cfg.cls = '';
573             cfg.cn =  [{
574                 tag : 'a',
575                 cls : 'roo-button',
576                 html : this.html,
577                 href : this.href || '#'
578             }];
579             if (this.menu) {
580                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
581                 cfg.cls += ' dropdown';
582             }   
583             
584             delete cfg.html;
585             
586         }
587         
588        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
589         
590         if (this.glyphicon) {
591             cfg.html = ' ' + cfg.html;
592             
593             cfg.cn = [
594                 {
595                     tag: 'span',
596                     cls: 'glyphicon glyphicon-' + this.glyphicon
597                 }
598             ];
599         }
600         
601         if (this.badge) {
602             cfg.html += ' ';
603             
604             cfg.tag = 'a';
605             
606 //            cfg.cls='btn roo-button';
607             
608             cfg.href=this.href;
609             
610             var value = cfg.html;
611             
612             if(this.glyphicon){
613                 value = {
614                             tag: 'span',
615                             cls: 'glyphicon glyphicon-' + this.glyphicon,
616                             html: this.html
617                         };
618                 
619             }
620             
621             cfg.cn = [
622                 value,
623                 {
624                     tag: 'span',
625                     cls: 'badge',
626                     html: this.badge
627                 }
628             ];
629             
630             cfg.html='';
631         }
632         
633         if (this.menu) {
634             cfg.cls += ' dropdown';
635             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
636         }
637         
638         if (cfg.tag !== 'a' && this.href !== '') {
639             throw "Tag must be a to set href.";
640         } else if (this.href.length > 0) {
641             cfg.href = this.href;
642         }
643         
644         if(this.removeClass){
645             cfg.cls = '';
646         }
647         
648         if(this.target){
649             cfg.target = this.target;
650         }
651         
652         return cfg;
653     },
654     initEvents: function() {
655        // Roo.log('init events?');
656 //        Roo.log(this.el.dom);
657        if (this.el.hasClass('roo-button')) {
658             this.el.on('click', this.onClick, this);
659        } else {
660             this.el.select('.roo-button').on('click', this.onClick, this);
661        }
662        
663        
664         
665     },
666     onClick : function(e)
667     {
668         if (this.disabled) {
669             return;
670         }
671         
672         Roo.log('button on click ');
673         if(this.preventDefault){
674             e.preventDefault();
675         }
676         if (this.pressed === true || this.pressed === false) {
677             this.pressed = !this.pressed;
678             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679             this.fireEvent('toggle', this, e, this.pressed);
680         }
681         
682         
683         this.fireEvent('click', this, e);
684     },
685     
686     /**
687      * Enables this button
688      */
689     enable : function()
690     {
691         this.disabled = false;
692         this.el.removeClass('disabled');
693     },
694     
695     /**
696      * Disable this button
697      */
698     disable : function()
699     {
700         this.disabled = true;
701         this.el.addClass('disabled');
702     },
703      /**
704      * sets the active state on/off, 
705      * @param {Boolean} state (optional) Force a particular state
706      */
707     setActive : function(v) {
708         
709         this.el[v ? 'addClass' : 'removeClass']('active');
710     },
711      /**
712      * toggles the current active state 
713      */
714     toggleActive : function()
715     {
716        var active = this.el.hasClass('active');
717        this.setActive(!active);
718        
719         
720     }
721     
722     
723     
724 });
725
726  /*
727  * - LGPL
728  *
729  * column
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.Column
735  * @extends Roo.bootstrap.Component
736  * Bootstrap Column class
737  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739  * @cfg {Number} md colspan out of 12 for computer-sized screens
740  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741  * @cfg {String} html content of column.
742  * 
743  * @constructor
744  * Create a new Column
745  * @param {Object} config The config object
746  */
747
748 Roo.bootstrap.Column = function(config){
749     Roo.bootstrap.Column.superclass.constructor.call(this, config);
750 };
751
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
753     
754     xs: null,
755     sm: null,
756     md: null,
757     lg: null,
758     html: '',
759     offset: 0,
760     
761     getAutoCreate : function(){
762         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
763         
764         cfg = {
765             tag: 'div',
766             cls: 'column'
767         };
768         
769         var settings=this;
770         ['xs','sm','md','lg'].map(function(size){
771             if (settings[size]) {
772                 cfg.cls += ' col-' + size + '-' + settings[size];
773             }
774         });
775         if (this.html.length) {
776             cfg.html = this.html;
777         }
778         
779         return cfg;
780     }
781    
782 });
783
784  
785
786  /*
787  * - LGPL
788  *
789  * page container.
790  * 
791  */
792
793
794 /**
795  * @class Roo.bootstrap.Container
796  * @extends Roo.bootstrap.Component
797  * Bootstrap Container class
798  * @cfg {Boolean} jumbotron is it a jumbotron element
799  * @cfg {String} html content of element
800  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802  * @cfg {String} header content of header (for panel)
803  * @cfg {String} footer content of footer (for panel)
804  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
805  *     
806  * @constructor
807  * Create a new Container
808  * @param {Object} config The config object
809  */
810
811 Roo.bootstrap.Container = function(config){
812     Roo.bootstrap.Container.superclass.constructor.call(this, config);
813 };
814
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
816     
817     jumbotron : false,
818     well: '',
819     panel : '',
820     header: '',
821     footer : '',
822     sticky: '',
823   
824      
825     getChildContainer : function() {
826         
827         if(!this.el){
828             return false;
829         }
830         
831         if (this.panel.length) {
832             return this.el.select('.panel-body',true).first();
833         }
834         
835         return this.el;
836     },
837     
838     
839     getAutoCreate : function(){
840         
841         var cfg = {
842             html : '',
843             cls : ''
844         };
845         if (this.jumbotron) {
846             cfg.cls = 'jumbotron';
847         }
848         if (this.cls) {
849             cfg.cls = this.cls + '';
850         }
851         
852         if (this.sticky.length) {
853             
854             var bd = Roo.get(document.body);
855             if (!bd.hasClass('bootstrap-sticky')) {
856                 bd.addClass('bootstrap-sticky');
857                 Roo.select('html',true).setStyle('height', '100%');
858             }
859              
860             cfg.cls += 'bootstrap-sticky-' + this.sticky;
861         }
862         
863         
864         if (this.well.length) {
865             switch (this.well) {
866                 case 'lg':
867                 case 'sm':
868                     cfg.cls +=' well well-' +this.well;
869                     break;
870                 default:
871                     cfg.cls +=' well';
872                     break;
873             }
874         }
875         
876         var body = cfg;
877         
878         if (this.panel.length) {
879             cfg.cls += ' panel panel-' + this.panel;
880             cfg.cn = [];
881             if (this.header.length) {
882                 cfg.cn.push({
883                     
884                     cls : 'panel-heading',
885                     cn : [{
886                         tag: 'h3',
887                         cls : 'panel-title',
888                         html : this.header
889                     }]
890                     
891                 });
892             }
893             body = false;
894             cfg.cn.push({
895                 cls : 'panel-body',
896                 html : this.html
897             });
898             
899             
900             if (this.footer.length) {
901                 cfg.cn.push({
902                     cls : 'panel-footer',
903                     html : this.footer
904                     
905                 });
906             }
907             
908         }
909         if (body) {
910             body.html = this.html || cfg.html;
911         }
912         if (!cfg.cls.length) {
913             cfg.cls =  'container';
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  /*
922  * - LGPL
923  *
924  * image
925  * 
926  */
927
928
929 /**
930  * @class Roo.bootstrap.Img
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Img class
933  * @cfg {Boolean} imgResponsive false | true
934  * @cfg {String} border rounded | circle | thumbnail
935  * @cfg {String} src image source
936  * @cfg {String} alt image alternative text
937  * @cfg {String} href a tag href
938  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
939  * 
940  * @constructor
941  * Create a new Input
942  * @param {Object} config The config object
943  */
944
945 Roo.bootstrap.Img = function(config){
946     Roo.bootstrap.Img.superclass.constructor.call(this, config);
947     
948     this.addEvents({
949         // img events
950         /**
951          * @event click
952          * The img click event for the img.
953          * @param {Roo.EventObject} e
954          */
955         "click" : true
956     });
957 };
958
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
960     
961     imgResponsive: true,
962     border: '',
963     src: '',
964     href: false,
965     target: false,
966
967     getAutoCreate : function(){
968         
969         var cfg = {
970             tag: 'img',
971             cls: 'img-responsive',
972             html : null
973         }
974         
975         cfg.html = this.html || cfg.html;
976         
977         cfg.src = this.src || cfg.src;
978         
979         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980             cfg.cls += ' img-' + this.border;
981         }
982         
983         if(this.alt){
984             cfg.alt = this.alt;
985         }
986         
987         if(this.href){
988             var a = {
989                 tag: 'a',
990                 href: this.href,
991                 cn: [
992                     cfg
993                 ]
994             }
995             
996             if(this.target){
997                 a.target = this.target;
998             }
999             
1000         }
1001         
1002         
1003         return (this.href) ? a : cfg;
1004     },
1005     
1006     initEvents: function() {
1007         
1008         if(!this.href){
1009             this.el.on('click', this.onClick, this);
1010         }
1011     },
1012     
1013     onClick : function(e)
1014     {
1015         Roo.log('img onclick');
1016         this.fireEvent('click', this, e);
1017     }
1018    
1019 });
1020
1021  /*
1022  * - LGPL
1023  *
1024  * header
1025  * 
1026  */
1027
1028 /**
1029  * @class Roo.bootstrap.Header
1030  * @extends Roo.bootstrap.Component
1031  * Bootstrap Header class
1032  * @cfg {String} html content of header
1033  * @cfg {Number} level (1|2|3|4|5|6) default 1
1034  * 
1035  * @constructor
1036  * Create a new Header
1037  * @param {Object} config The config object
1038  */
1039
1040
1041 Roo.bootstrap.Header  = function(config){
1042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1046     
1047     //href : false,
1048     html : false,
1049     level : 1,
1050     
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag: 'h' + (1 *this.level),
1057             html: this.html || 'fill in html'
1058         } ;
1059         
1060         return cfg;
1061     }
1062    
1063 });
1064
1065  
1066
1067  /*
1068  * Based on:
1069  * Ext JS Library 1.1.1
1070  * Copyright(c) 2006-2007, Ext JS, LLC.
1071  *
1072  * Originally Released Under LGPL - original licence link has changed is not relivant.
1073  *
1074  * Fork - LGPL
1075  * <script type="text/javascript">
1076  */
1077  
1078 /**
1079  * @class Roo.bootstrap.MenuMgr
1080  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1081  * @singleton
1082  */
1083 Roo.bootstrap.MenuMgr = function(){
1084    var menus, active, groups = {}, attached = false, lastShow = new Date();
1085
1086    // private - called when first menu is created
1087    function init(){
1088        menus = {};
1089        active = new Roo.util.MixedCollection();
1090        Roo.get(document).addKeyListener(27, function(){
1091            if(active.length > 0){
1092                hideAll();
1093            }
1094        });
1095    }
1096
1097    // private
1098    function hideAll(){
1099        if(active && active.length > 0){
1100            var c = active.clone();
1101            c.each(function(m){
1102                m.hide();
1103            });
1104        }
1105    }
1106
1107    // private
1108    function onHide(m){
1109        active.remove(m);
1110        if(active.length < 1){
1111            Roo.get(document).un("mouseup", onMouseDown);
1112             
1113            attached = false;
1114        }
1115    }
1116
1117    // private
1118    function onShow(m){
1119        var last = active.last();
1120        lastShow = new Date();
1121        active.add(m);
1122        if(!attached){
1123           Roo.get(document).on("mouseup", onMouseDown);
1124            
1125            attached = true;
1126        }
1127        if(m.parentMenu){
1128           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129           m.parentMenu.activeChild = m;
1130        }else if(last && last.isVisible()){
1131           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1132        }
1133    }
1134
1135    // private
1136    function onBeforeHide(m){
1137        if(m.activeChild){
1138            m.activeChild.hide();
1139        }
1140        if(m.autoHideTimer){
1141            clearTimeout(m.autoHideTimer);
1142            delete m.autoHideTimer;
1143        }
1144    }
1145
1146    // private
1147    function onBeforeShow(m){
1148        var pm = m.parentMenu;
1149        if(!pm && !m.allowOtherMenus){
1150            hideAll();
1151        }else if(pm && pm.activeChild && active != m){
1152            pm.activeChild.hide();
1153        }
1154    }
1155
1156    // private
1157    function onMouseDown(e){
1158         Roo.log("on MouseDown");
1159         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1160            hideAll();
1161         }
1162         
1163         
1164    }
1165
1166    // private
1167    function onBeforeCheck(mi, state){
1168        if(state){
1169            var g = groups[mi.group];
1170            for(var i = 0, l = g.length; i < l; i++){
1171                if(g[i] != mi){
1172                    g[i].setChecked(false);
1173                }
1174            }
1175        }
1176    }
1177
1178    return {
1179
1180        /**
1181         * Hides all menus that are currently visible
1182         */
1183        hideAll : function(){
1184             hideAll();  
1185        },
1186
1187        // private
1188        register : function(menu){
1189            if(!menus){
1190                init();
1191            }
1192            menus[menu.id] = menu;
1193            menu.on("beforehide", onBeforeHide);
1194            menu.on("hide", onHide);
1195            menu.on("beforeshow", onBeforeShow);
1196            menu.on("show", onShow);
1197            var g = menu.group;
1198            if(g && menu.events["checkchange"]){
1199                if(!groups[g]){
1200                    groups[g] = [];
1201                }
1202                groups[g].push(menu);
1203                menu.on("checkchange", onCheck);
1204            }
1205        },
1206
1207         /**
1208          * Returns a {@link Roo.menu.Menu} object
1209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210          * be used to generate and return a new Menu instance.
1211          */
1212        get : function(menu){
1213            if(typeof menu == "string"){ // menu id
1214                return menus[menu];
1215            }else if(menu.events){  // menu instance
1216                return menu;
1217            }
1218            /*else if(typeof menu.length == 'number'){ // array of menu items?
1219                return new Roo.bootstrap.Menu({items:menu});
1220            }else{ // otherwise, must be a config
1221                return new Roo.bootstrap.Menu(menu);
1222            }
1223            */
1224            return false;
1225        },
1226
1227        // private
1228        unregister : function(menu){
1229            delete menus[menu.id];
1230            menu.un("beforehide", onBeforeHide);
1231            menu.un("hide", onHide);
1232            menu.un("beforeshow", onBeforeShow);
1233            menu.un("show", onShow);
1234            var g = menu.group;
1235            if(g && menu.events["checkchange"]){
1236                groups[g].remove(menu);
1237                menu.un("checkchange", onCheck);
1238            }
1239        },
1240
1241        // private
1242        registerCheckable : function(menuItem){
1243            var g = menuItem.group;
1244            if(g){
1245                if(!groups[g]){
1246                    groups[g] = [];
1247                }
1248                groups[g].push(menuItem);
1249                menuItem.on("beforecheckchange", onBeforeCheck);
1250            }
1251        },
1252
1253        // private
1254        unregisterCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                groups[g].remove(menuItem);
1258                menuItem.un("beforecheckchange", onBeforeCheck);
1259            }
1260        }
1261    };
1262 }();/*
1263  * - LGPL
1264  *
1265  * menu
1266  * 
1267  */
1268
1269 /**
1270  * @class Roo.bootstrap.Menu
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Menu class - container for MenuItems
1273  * @cfg {String} type type of menu
1274  * 
1275  * @constructor
1276  * Create a new Menu
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Menu = function(config){
1282     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283     if (this.registerMenu) {
1284         Roo.bootstrap.MenuMgr.register(this);
1285     }
1286     this.addEvents({
1287         /**
1288          * @event beforeshow
1289          * Fires before this menu is displayed
1290          * @param {Roo.menu.Menu} this
1291          */
1292         beforeshow : true,
1293         /**
1294          * @event beforehide
1295          * Fires before this menu is hidden
1296          * @param {Roo.menu.Menu} this
1297          */
1298         beforehide : true,
1299         /**
1300          * @event show
1301          * Fires after this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         show : true,
1305         /**
1306          * @event hide
1307          * Fires after this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         hide : true,
1311         /**
1312          * @event click
1313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314          * @param {Roo.menu.Menu} this
1315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316          * @param {Roo.EventObject} e
1317          */
1318         click : true,
1319         /**
1320          * @event mouseover
1321          * Fires when the mouse is hovering over this menu
1322          * @param {Roo.menu.Menu} this
1323          * @param {Roo.EventObject} e
1324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1325          */
1326         mouseover : true,
1327         /**
1328          * @event mouseout
1329          * Fires when the mouse exits this menu
1330          * @param {Roo.menu.Menu} this
1331          * @param {Roo.EventObject} e
1332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1333          */
1334         mouseout : true,
1335         /**
1336          * @event itemclick
1337          * Fires when a menu item contained in this menu is clicked
1338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339          * @param {Roo.EventObject} e
1340          */
1341         itemclick: true
1342     });
1343     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1347     
1348    /// html : false,
1349     //align : '',
1350     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1351     type: false,
1352     /**
1353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1354      */
1355     registerMenu : true,
1356     
1357     menuItems :false, // stores the menu items..
1358     
1359     hidden:true,
1360     
1361     parentMenu : false,
1362     
1363     getChildContainer : function() {
1364         return this.el;  
1365     },
1366     
1367     getAutoCreate : function(){
1368          
1369         //if (['right'].indexOf(this.align)!==-1) {
1370         //    cfg.cn[1].cls += ' pull-right'
1371         //}
1372         var cfg = {
1373             tag : 'ul',
1374             cls : 'dropdown-menu' ,
1375             style : 'z-index:1000'
1376             
1377         }
1378         
1379         if (this.type === 'submenu') {
1380             cfg.cls = 'submenu active'
1381         }
1382         
1383         return cfg;
1384     },
1385     initEvents : function() {
1386         
1387        // Roo.log("ADD event");
1388        // Roo.log(this.triggerEl.dom);
1389         this.triggerEl.on('click', this.onTriggerPress, this);
1390         this.triggerEl.addClass('dropdown-toggle');
1391         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1392
1393         this.el.on("mouseover", this.onMouseOver, this);
1394         this.el.on("mouseout", this.onMouseOut, this);
1395         
1396         
1397     },
1398     findTargetItem : function(e){
1399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1400         if(!t){
1401             return false;
1402         }
1403         //Roo.log(t);         Roo.log(t.id);
1404         if(t && t.id){
1405             //Roo.log(this.menuitems);
1406             return this.menuitems.get(t.id);
1407             
1408             //return this.items.get(t.menuItemId);
1409         }
1410         
1411         return false;
1412     },
1413     onClick : function(e){
1414         Roo.log("menu.onClick");
1415         var t = this.findTargetItem(e);
1416         if(!t){
1417             return;
1418         }
1419         Roo.log(e);
1420         /*
1421         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1422             if(t == this.activeItem && t.shouldDeactivate(e)){
1423                 this.activeItem.deactivate();
1424                 delete this.activeItem;
1425                 return;
1426             }
1427             if(t.canActivate){
1428                 this.setActiveItem(t, true);
1429             }
1430             return;
1431             
1432             
1433         }
1434         */
1435         Roo.log('pass click event');
1436         
1437         t.onClick(e);
1438         
1439         this.fireEvent("click", this, t, e);
1440         
1441         this.hide();
1442     },
1443      onMouseOver : function(e){
1444         var t  = this.findTargetItem(e);
1445         //Roo.log(t);
1446         //if(t){
1447         //    if(t.canActivate && !t.disabled){
1448         //        this.setActiveItem(t, true);
1449         //    }
1450         //}
1451         
1452         this.fireEvent("mouseover", this, e, t);
1453     },
1454     isVisible : function(){
1455         return !this.hidden;
1456     },
1457      onMouseOut : function(e){
1458         var t  = this.findTargetItem(e);
1459         
1460         //if(t ){
1461         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1462         //        this.activeItem.deactivate();
1463         //        delete this.activeItem;
1464         //    }
1465         //}
1466         this.fireEvent("mouseout", this, e, t);
1467     },
1468     
1469     
1470     /**
1471      * Displays this menu relative to another element
1472      * @param {String/HTMLElement/Roo.Element} element The element to align to
1473      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474      * the element (defaults to this.defaultAlign)
1475      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1476      */
1477     show : function(el, pos, parentMenu){
1478         this.parentMenu = parentMenu;
1479         if(!this.el){
1480             this.render();
1481         }
1482         this.fireEvent("beforeshow", this);
1483         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1484     },
1485      /**
1486      * Displays this menu at a specific xy position
1487      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489      */
1490     showAt : function(xy, parentMenu, /* private: */_e){
1491         this.parentMenu = parentMenu;
1492         if(!this.el){
1493             this.render();
1494         }
1495         if(_e !== false){
1496             this.fireEvent("beforeshow", this);
1497             
1498             //xy = this.el.adjustForConstraints(xy);
1499         }
1500         //this.el.setXY(xy);
1501         //this.el.show();
1502         this.hideMenuItems();
1503         this.hidden = false;
1504         this.triggerEl.addClass('open');
1505         this.focus();
1506         this.fireEvent("show", this);
1507     },
1508     
1509     focus : function(){
1510         return;
1511         if(!this.hidden){
1512             this.doFocus.defer(50, this);
1513         }
1514     },
1515
1516     doFocus : function(){
1517         if(!this.hidden){
1518             this.focusEl.focus();
1519         }
1520     },
1521
1522     /**
1523      * Hides this menu and optionally all parent menus
1524      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1525      */
1526     hide : function(deep){
1527         
1528         this.hideMenuItems();
1529         if(this.el && this.isVisible()){
1530             this.fireEvent("beforehide", this);
1531             if(this.activeItem){
1532                 this.activeItem.deactivate();
1533                 this.activeItem = null;
1534             }
1535             this.triggerEl.removeClass('open');;
1536             this.hidden = true;
1537             this.fireEvent("hide", this);
1538         }
1539         if(deep === true && this.parentMenu){
1540             this.parentMenu.hide(true);
1541         }
1542     },
1543     
1544     onTriggerPress  : function(e)
1545     {
1546         
1547         Roo.log('trigger press');
1548         //Roo.log(e.getTarget());
1549        // Roo.log(this.triggerEl.dom);
1550         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1551             return;
1552         }
1553         if (this.isVisible()) {
1554             Roo.log('hide');
1555             this.hide();
1556         } else {
1557             this.show(this.triggerEl, false, false);
1558         }
1559         
1560         
1561     },
1562     
1563          
1564        
1565     
1566     hideMenuItems : function()
1567     {
1568         //$(backdrop).remove()
1569         Roo.select('.open',true).each(function(aa) {
1570             
1571             aa.removeClass('open');
1572           //var parent = getParent($(this))
1573           //var relatedTarget = { relatedTarget: this }
1574           
1575            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576           //if (e.isDefaultPrevented()) return
1577            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1578         })
1579     },
1580     addxtypeChild : function (tree, cntr) {
1581         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1582           
1583         this.menuitems.add(comp);
1584         return comp;
1585
1586     },
1587     getEl : function()
1588     {
1589         Roo.log(this.el);
1590         return this.el;
1591     }
1592 });
1593
1594  
1595  /*
1596  * - LGPL
1597  *
1598  * menu item
1599  * 
1600  */
1601
1602
1603 /**
1604  * @class Roo.bootstrap.MenuItem
1605  * @extends Roo.bootstrap.Component
1606  * Bootstrap MenuItem class
1607  * @cfg {String} html the menu label
1608  * @cfg {String} href the link
1609  * @cfg {Boolean} preventDefault (true | false) default true
1610  * 
1611  * 
1612  * @constructor
1613  * Create a new MenuItem
1614  * @param {Object} config The config object
1615  */
1616
1617
1618 Roo.bootstrap.MenuItem = function(config){
1619     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1620     this.addEvents({
1621         // raw events
1622         /**
1623          * @event click
1624          * The raw click event for the entire grid.
1625          * @param {Roo.EventObject} e
1626          */
1627         "click" : true
1628     });
1629 };
1630
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1632     
1633     href : false,
1634     html : false,
1635     preventDefault: true,
1636     
1637     getAutoCreate : function(){
1638         var cfg= {
1639             tag: 'li',
1640         cls: 'dropdown-menu-item',
1641             cn: [
1642             {
1643                 tag : 'a',
1644                 href : '#',
1645                 html : 'Link'
1646             }
1647             ]
1648     };
1649         
1650         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1652         return cfg;
1653     },
1654     
1655     initEvents: function() {
1656         
1657         //this.el.select('a').on('click', this.onClick, this);
1658         
1659     },
1660     onClick : function(e)
1661     {
1662         Roo.log('item on click ');
1663         //if(this.preventDefault){
1664         //    e.preventDefault();
1665         //}
1666         //this.parent().hideMenuItems();
1667         
1668         this.fireEvent('click', this, e);
1669     },
1670     getEl : function()
1671     {
1672         return this.el;
1673     }
1674 });
1675
1676  
1677
1678  /*
1679  * - LGPL
1680  *
1681  * menu separator
1682  * 
1683  */
1684
1685
1686 /**
1687  * @class Roo.bootstrap.MenuSeparator
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap MenuSeparator class
1690  * 
1691  * @constructor
1692  * Create a new MenuItem
1693  * @param {Object} config The config object
1694  */
1695
1696
1697 Roo.bootstrap.MenuSeparator = function(config){
1698     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1699 };
1700
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1702     
1703     getAutoCreate : function(){
1704         var cfg = {
1705             cls: 'divider',
1706             tag : 'li'
1707         };
1708         
1709         return cfg;
1710     }
1711    
1712 });
1713
1714  
1715
1716  
1717 /*
1718 <div class="modal fade">
1719   <div class="modal-dialog">
1720     <div class="modal-content">
1721       <div class="modal-header">
1722         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1723         <h4 class="modal-title">Modal title</h4>
1724       </div>
1725       <div class="modal-body">
1726         <p>One fine body&hellip;</p>
1727       </div>
1728       <div class="modal-footer">
1729         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730         <button type="button" class="btn btn-primary">Save changes</button>
1731       </div>
1732     </div><!-- /.modal-content -->
1733   </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1735 */
1736 /*
1737  * - LGPL
1738  *
1739  * page contgainer.
1740  * 
1741  */
1742
1743 /**
1744  * @class Roo.bootstrap.Modal
1745  * @extends Roo.bootstrap.Component
1746  * Bootstrap Modal class
1747  * @cfg {String} title Title of dialog
1748  * @cfg {Array} buttons Array of buttons or standard button set..
1749  * 
1750  * @constructor
1751  * Create a new Modal Dialog
1752  * @param {Object} config The config object
1753  */
1754
1755 Roo.bootstrap.Modal = function(config){
1756     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1757     this.addEvents({
1758         // raw events
1759         /**
1760          * @event btnclick
1761          * The raw btnclick event for the button
1762          * @param {Roo.EventObject} e
1763          */
1764         "btnclick" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1769     
1770     title : 'test dialog',
1771    
1772     buttons : false,
1773     
1774     onRender : function(ct, position)
1775     {
1776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1777      
1778         if(!this.el){
1779             var cfg = Roo.apply({},  this.getAutoCreate());
1780             cfg.id = Roo.id();
1781             //if(!cfg.name){
1782             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1783             //}
1784             //if (!cfg.name.length) {
1785             //    delete cfg.name;
1786            // }
1787             if (this.cls) {
1788                 cfg.cls += ' ' + this.cls;
1789             }
1790             if (this.style) {
1791                 cfg.style = this.style;
1792             }
1793             this.el = Roo.get(document.body).createChild(cfg, position);
1794         }
1795         //var type = this.el.dom.type;
1796         
1797         if(this.tabIndex !== undefined){
1798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1799         }
1800         
1801         
1802         
1803         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804         this.maskEl.enableDisplayMode("block");
1805         this.maskEl.hide();
1806         //this.el.addClass("x-dlg-modal");
1807     
1808         if (this.buttons) {
1809             Roo.each(this.buttons, function(bb) {
1810                 b = Roo.apply({}, bb);
1811                 b.xns = b.xns || Roo.bootstrap;
1812                 b.xtype = b.xtype || 'Button';
1813                 if (typeof(b.listeners) == 'undefined') {
1814                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1815                 }
1816                 
1817                 var btn = Roo.factory(b);
1818                 
1819                 btn.onRender(this.el.select('.modal-footer').first());
1820                 
1821             },this);
1822         }
1823         // render the children.
1824         var nitems = [];
1825         
1826         if(typeof(this.items) != 'undefined'){
1827             var items = this.items;
1828             delete this.items;
1829
1830             for(var i =0;i < items.length;i++) {
1831                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1832             }
1833         }
1834         
1835         this.items = nitems;
1836         this.initEvents();
1837         //this.el.addClass([this.fieldClass, this.cls]);
1838         
1839     },
1840     getAutoCreate : function(){
1841         
1842         
1843         var bdy = {
1844                 cls : 'modal-body',
1845                 html : this.html || ''
1846         };
1847         
1848          
1849         return modal = {
1850             cls: "modal fade",
1851             cn : [
1852                 {
1853                     cls: "modal-dialog",
1854                     cn : [
1855                         {
1856                             cls : "modal-content",
1857                             cn : [
1858                                 {
1859                                     cls : 'modal-header',
1860                                     cn : [
1861                                         {
1862                                             tag: 'button',
1863                                             cls : 'close',
1864                                             html : '&times'
1865                                         },
1866                                         {
1867                                             tag: 'h4',
1868                                             cls : 'modal-title',
1869                                             html : this.title
1870                                         }
1871                                     
1872                                     ]
1873                                 },
1874                                 bdy,
1875                                 {
1876                                     cls : 'modal-footer' 
1877                                 }
1878                                 
1879                                 
1880                             ]
1881                             
1882                         }
1883                     ]
1884                         
1885                 }
1886             ]
1887             
1888             
1889         };
1890           
1891     },
1892     getChildContainer : function() {
1893          
1894          return this.el.select('.modal-body',true).first();
1895         
1896     },
1897     getButtonContainer : function() {
1898          return this.el.select('.modal-footer',true).first();
1899         
1900     },
1901     initEvents : function()
1902     {
1903         this.el.select('.modal-header .close').on('click', this.hide, this);
1904 //        
1905 //        this.addxtype(this);
1906     },
1907     show : function() {
1908         
1909         if (!this.rendered) {
1910             this.render();
1911         }
1912        
1913         this.el.addClass('on');
1914         this.el.removeClass('fade');
1915         this.el.setStyle('display', 'block');
1916         Roo.get(document.body).addClass("x-body-masked");
1917         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1918         this.maskEl.show();
1919         this.el.setStyle('zIndex', '10001');
1920         this.fireEvent('show', this);
1921         
1922         
1923     },
1924     hide : function()
1925     {
1926         Roo.log('Modal hide?!');
1927         this.maskEl.hide();
1928         Roo.get(document.body).removeClass("x-body-masked");
1929         this.el.removeClass('on');
1930         this.el.addClass('fade');
1931         this.el.setStyle('display', 'none');
1932         this.fireEvent('hide', this);
1933     },
1934     onButtonClick: function(btn,e)
1935     {
1936         //Roo.log([a,b,c]);
1937         this.fireEvent('btnclick', btn.name, e);
1938     }
1939 });
1940
1941
1942 Roo.apply(Roo.bootstrap.Modal,  {
1943     /**
1944          * Button config that displays a single OK button
1945          * @type Object
1946          */
1947         OK :  [{
1948             name : 'ok',
1949             weight : 'primary',
1950             html : 'OK'
1951         }], 
1952         /**
1953          * Button config that displays Yes and No buttons
1954          * @type Object
1955          */
1956         YESNO : [
1957             {
1958                 name  : 'no',
1959                 html : 'No'
1960             },
1961             {
1962                 name  :'yes',
1963                 weight : 'primary',
1964                 html : 'Yes'
1965             }
1966         ],
1967         
1968         /**
1969          * Button config that displays OK and Cancel buttons
1970          * @type Object
1971          */
1972         OKCANCEL : [
1973             {
1974                name : 'cancel',
1975                 html : 'Cancel'
1976             },
1977             {
1978                 name : 'ok',
1979                 weight : 'primary',
1980                 html : 'OK'
1981             }
1982         ],
1983         /**
1984          * Button config that displays Yes, No and Cancel buttons
1985          * @type Object
1986          */
1987         YESNOCANCEL : [
1988             {
1989                 name : 'yes',
1990                 weight : 'primary',
1991                 html : 'Yes'
1992             },
1993             {
1994                 name : 'no',
1995                 html : 'No'
1996             },
1997             {
1998                 name : 'cancel',
1999                 html : 'Cancel'
2000             }
2001         ]
2002 });
2003  /*
2004  * - LGPL
2005  *
2006  * navbar
2007  * 
2008  */
2009
2010 /**
2011  * @class Roo.bootstrap.Navbar
2012  * @extends Roo.bootstrap.Component
2013  * Bootstrap Navbar class
2014  * @cfg {Boolean} sidebar has side bar
2015  * @cfg {Boolean} bar is a bar?
2016  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017  * @cfg {String} brand what is brand
2018  * @cfg {Boolean} inverse is inverted color
2019  * @cfg {String} type (nav | pills | tabs)
2020  * @cfg {Boolean} arrangement stacked | justified
2021  * @cfg {String} align (left | right) alignment
2022  * @cfg {String} brand_href href of the brand
2023  * @cfg {Boolean} main (true|false) main nav bar? default false
2024  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2025  *
2026  * 
2027  * @constructor
2028  * Create a new Navbar
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.Navbar = function(config){
2034     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2038     
2039     sidebar: false,
2040     
2041     bar: false,
2042     brand: '',
2043     inverse: false,
2044     position: '',
2045     align : false,
2046     type: 'nav',
2047     arrangement: '',
2048     brand_href: false,
2049     main : false,
2050     loadMask : false,
2051     
2052     getAutoCreate : function(){
2053         var cfg = {
2054             cls : 'navbar'
2055         };
2056         
2057         if (this.sidebar === true) {
2058             cfg = {
2059                 tag: 'div',
2060                 cls: 'sidebar-nav'
2061             };
2062             return cfg;
2063         }
2064         
2065         if (this.bar === true) {
2066             cfg = {
2067                 tag: 'nav',
2068                 cls: 'navbar',
2069                 role: 'navigation',
2070                 cn: [
2071                     {
2072                         tag: 'div',
2073                         cls: 'navbar-header',
2074                         cn: [
2075                             {
2076                             tag: 'button',
2077                             type: 'button',
2078                             cls: 'navbar-toggle',
2079                             'data-toggle': 'collapse',
2080                             cn: [
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'sr-only',
2084                                     html: 'Toggle navigation'
2085                                 },
2086                                 {
2087                                     tag: 'span',
2088                                     cls: 'icon-bar'
2089                                 },
2090                                 {
2091                                     tag: 'span',
2092                                     cls: 'icon-bar'
2093                                 },
2094                                 {
2095                                     tag: 'span',
2096                                     cls: 'icon-bar'
2097                                 }
2098                             ]
2099                             }
2100                         ]
2101                     },
2102                     {
2103                     tag: 'div',
2104                     cls: 'collapse navbar-collapse'
2105                     }
2106                 ]
2107             };
2108             
2109             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2110             
2111             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112                 cfg.cls += ' navbar-' + this.position;
2113                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2114             }
2115             
2116             if (this.brand !== '') {
2117                 cfg.cn[0].cn.push({
2118                     tag: 'a',
2119                     href: this.brand_href ? this.brand_href : '#',
2120                     cls: 'navbar-brand',
2121                     cn: [
2122                     this.brand
2123                     ]
2124                 });
2125             }
2126             
2127             if(this.main){
2128                 cfg.cls += ' main-nav';
2129             }
2130             
2131             
2132             return cfg;
2133         
2134         } else if (this.bar === false) {
2135             
2136         } else {
2137             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2138         }
2139         
2140         cfg.cn = [
2141             {
2142                 cls: 'nav',
2143                 tag : 'ul'
2144             }
2145         ];
2146         
2147         if (['tabs','pills'].indexOf(this.type)!==-1) {
2148             cfg.cn[0].cls += ' nav-' + this.type
2149         } else {
2150             if (this.type!=='nav') {
2151             Roo.log('nav type must be nav/tabs/pills')
2152             }
2153             cfg.cn[0].cls += ' navbar-nav'
2154         }
2155         
2156         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157             cfg.cn[0].cls += ' nav-' + this.arrangement;
2158         }
2159         
2160         if (this.align === 'right') {
2161             cfg.cn[0].cls += ' navbar-right';
2162         }
2163         if (this.inverse) {
2164             cfg.cls += ' navbar-inverse';
2165             
2166         }
2167         
2168         
2169         return cfg;
2170     },
2171     
2172     initEvents :function ()
2173     {
2174         //Roo.log(this.el.select('.navbar-toggle',true));
2175         this.el.select('.navbar-toggle',true).on('click', function() {
2176            // Roo.log('click');
2177             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2178         }, this);
2179         
2180         var mark = {
2181             tag: "div",
2182             cls:"x-dlg-mask"
2183         }
2184         
2185         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2186         
2187         var size = this.el.getSize();
2188         this.maskEl.setSize(size.width, size.height);
2189         this.maskEl.enableDisplayMode("block");
2190         this.maskEl.hide();
2191         
2192         if(this.loadMask){
2193             this.maskEl.show();
2194         }
2195     },
2196     
2197     
2198     getChildContainer : function()
2199     {
2200         if (this.bar === true) {
2201             return this.el.select('.collapse',true).first();
2202         }
2203         
2204         return this.el;
2205     },
2206     
2207     mask : function()
2208     {
2209         this.maskEl.show();
2210     },
2211     
2212     unmask : function()
2213     {
2214         this.maskEl.hide();
2215     }
2216    
2217 });
2218
2219  
2220
2221  /*
2222  * - LGPL
2223  *
2224  * nav group
2225  * 
2226  */
2227
2228 /**
2229  * @class Roo.bootstrap.NavGroup
2230  * @extends Roo.bootstrap.Component
2231  * Bootstrap NavGroup class
2232  * @cfg {String} align left | right
2233  * @cfg {Boolean} inverse false | true
2234  * @cfg {String} type (nav|pills|tab) default nav
2235  * 
2236  * @constructor
2237  * Create a new nav group
2238  * @param {Object} config The config object
2239  */
2240
2241 Roo.bootstrap.NavGroup = function(config){
2242     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2243 };
2244
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2246     
2247     align: '',
2248     inverse: false,
2249     form: false,
2250     type: 'nav',
2251     
2252     getAutoCreate : function(){
2253         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2254         
2255         cfg = {
2256             tag : 'ul',
2257             cls: 'nav' 
2258         }
2259         
2260         if (['tabs','pills'].indexOf(this.type)!==-1) {
2261             cfg.cls += ' nav-' + this.type
2262         } else {
2263             if (this.type!=='nav') {
2264                 Roo.log('nav type must be nav/tabs/pills')
2265             }
2266             cfg.cls += ' navbar-nav'
2267         }
2268         
2269         if (this.parent().sidebar === true) {
2270             cfg = {
2271                 tag: 'ul',
2272                 cls: 'dashboard-menu'
2273             }
2274             
2275             return cfg;
2276         }
2277         
2278         if (this.form === true) {
2279             cfg = {
2280                 tag: 'form',
2281                 cls: 'navbar-form'
2282             }
2283             
2284             if (this.align === 'right') {
2285                 cfg.cls += ' navbar-right';
2286             } else {
2287                 cfg.cls += ' navbar-left';
2288             }
2289         }
2290         
2291         if (this.align === 'right') {
2292             cfg.cls += ' navbar-right';
2293         }
2294         
2295         if (this.inverse) {
2296             cfg.cls += ' navbar-inverse';
2297             
2298         }
2299         
2300         
2301         return cfg;
2302     }
2303    
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * row
2312  * 
2313  */
2314
2315 /**
2316  * @class Roo.bootstrap.Navbar.Item
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap Navbar.Button class
2319  * @cfg {String} href  link to
2320  * @cfg {String} html content of button
2321  * @cfg {String} badge text inside badge
2322  * @cfg {String} glyphicon name of glyphicon
2323  * @cfg {String} icon name of font awesome icon
2324  * @cfg {Boolena} active Is item active
2325  * @cfg {Boolean} preventDefault (true | false) default false
2326   
2327  * @constructor
2328  * Create a new Navbar Button
2329  * @param {Object} config The config object
2330  */
2331 Roo.bootstrap.Navbar.Item = function(config){
2332     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2333     this.addEvents({
2334         // raw events
2335         /**
2336          * @event click
2337          * The raw click event for the entire grid.
2338          * @param {Roo.EventObject} e
2339          */
2340         "click" : true
2341     });
2342 };
2343
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2345     
2346     href: false,
2347     html: '',
2348     badge: '',
2349     icon: false,
2350     glyphicon: false,
2351     icon: false,
2352     active: false,
2353     preventDefault : false,
2354     
2355     getAutoCreate : function(){
2356         
2357         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2358         
2359         if (this.parent().parent().sidebar === true) {
2360             cfg = {
2361                 tag: 'li',
2362                 cls: '',
2363                 cn: [
2364                     {
2365                         tag: 'p',
2366                         cls: ''
2367                     }
2368                 ]
2369             }
2370             
2371             if (this.html) {
2372                 cfg.cn[0].html = this.html;
2373             }
2374             
2375             if (this.active) {
2376                 this.cls += ' active';
2377             }
2378             
2379             if (this.menu) {
2380                 cfg.cn[0].cls += ' dropdown-toggle';
2381                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2382             }
2383             
2384             if (this.href) {
2385                 cfg.cn[0].tag = 'a',
2386                 cfg.cn[0].href = this.href;
2387             }
2388             
2389             if (this.glyphicon) {
2390                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2391             }
2392             
2393             if (this.icon) {
2394                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2395             }
2396             
2397             return cfg;
2398         }
2399         
2400         cfg = {
2401             tag: 'li',
2402             cls: 'nav-item'
2403         }
2404         
2405         if (this.active) {
2406             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2407         }
2408             
2409         cfg.cn = [
2410             {
2411                 tag: 'p',
2412                 html: 'Text'
2413             }
2414         ];
2415         
2416         if (this.glyphicon) {
2417             if(cfg.html){cfg.html = ' ' + this.html};
2418             cfg.cn=[
2419                 {
2420                     tag: 'span',
2421                     cls: 'glyphicon glyphicon-' + this.glyphicon
2422                 }
2423             ];
2424         }
2425         
2426         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2427         
2428         if (this.menu) {
2429             cfg.cn[0].tag='a';
2430             cfg.cn[0].href='#';
2431             cfg.cn[0].html += " <span class='caret'></span>";
2432         //}else if (!this.href) {
2433         //    cfg.cn[0].tag='p';
2434         //    cfg.cn[0].cls='navbar-text';
2435         } else {
2436             cfg.cn[0].tag='a';
2437             cfg.cn[0].href=this.href||'#';
2438             cfg.cn[0].html=this.html;
2439         }
2440         
2441         if (this.badge !== '') {
2442             
2443             cfg.cn[0].cn=[
2444                 cfg.cn[0].html + ' ',
2445                 {
2446                     tag: 'span',
2447                     cls: 'badge',
2448                     html: this.badge
2449                 }
2450             ];
2451             cfg.cn[0].html=''
2452         }
2453          
2454         if (this.icon) {
2455             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2456         }
2457         
2458         return cfg;
2459     },
2460     initEvents: function() {
2461        // Roo.log('init events?');
2462        // Roo.log(this.el.dom);
2463         this.el.select('a',true).on('click', this.onClick, this);
2464     },
2465     
2466     onClick : function(e)
2467     {
2468         if(this.preventDefault){
2469             e.preventDefault();
2470         }
2471         
2472         if(this.fireEvent('click', this, e) === false){
2473             return;
2474         };
2475         
2476         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477             this.onTabsClick(e);
2478         } 
2479     },
2480     
2481     onTabsClick : function(e)
2482     {
2483         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484             v.removeClass('active');
2485         })
2486
2487         this.el.addClass('active');
2488
2489         if(this.href && this.href.substring(0,1) == '#'){
2490             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2491
2492             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493                 v.removeClass('active');
2494             });
2495
2496             tab.addClass('active');
2497         }
2498     }
2499    
2500 });
2501  
2502
2503  /*
2504  * - LGPL
2505  *
2506  * row
2507  * 
2508  */
2509
2510 /**
2511  * @class Roo.bootstrap.Row
2512  * @extends Roo.bootstrap.Component
2513  * Bootstrap Row class (contains columns...)
2514  * 
2515  * @constructor
2516  * Create a new Row
2517  * @param {Object} config The config object
2518  */
2519
2520 Roo.bootstrap.Row = function(config){
2521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2525     
2526     getAutoCreate : function(){
2527        return {
2528             cls: 'row clearfix'
2529        };
2530     }
2531     
2532     
2533 });
2534
2535  
2536
2537  /*
2538  * - LGPL
2539  *
2540  * element
2541  * 
2542  */
2543
2544 /**
2545  * @class Roo.bootstrap.Element
2546  * @extends Roo.bootstrap.Component
2547  * Bootstrap Element class
2548  * @cfg {String} html contents of the element
2549  * @cfg {String} tag tag of the element
2550  * @cfg {String} cls class of the element
2551  * 
2552  * @constructor
2553  * Create a new Element
2554  * @param {Object} config The config object
2555  */
2556
2557 Roo.bootstrap.Element = function(config){
2558     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2559 };
2560
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2562     
2563     tag: 'div',
2564     cls: '',
2565     html: '',
2566      
2567     
2568     getAutoCreate : function(){
2569         
2570         var cfg = {
2571             tag: this.tag,
2572             cls: this.cls,
2573             html: this.html
2574         }
2575         
2576         
2577         
2578         return cfg;
2579     }
2580    
2581 });
2582
2583  
2584
2585  /*
2586  * - LGPL
2587  *
2588  * pagination
2589  * 
2590  */
2591
2592 /**
2593  * @class Roo.bootstrap.Pagination
2594  * @extends Roo.bootstrap.Component
2595  * Bootstrap Pagination class
2596  * @cfg {String} size xs | sm | md | lg
2597  * @cfg {Boolean} inverse false | true
2598  * 
2599  * @constructor
2600  * Create a new Pagination
2601  * @param {Object} config The config object
2602  */
2603
2604 Roo.bootstrap.Pagination = function(config){
2605     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2609     
2610     cls: false,
2611     size: false,
2612     inverse: false,
2613     
2614     getAutoCreate : function(){
2615         var cfg = {
2616             tag: 'ul',
2617                 cls: 'pagination'
2618         };
2619         if (this.inverse) {
2620             cfg.cls += ' inverse';
2621         }
2622         if (this.html) {
2623             cfg.html=this.html;
2624         }
2625         if (this.cls) {
2626             cfg.cls += " " + this.cls;
2627         }
2628         return cfg;
2629     }
2630    
2631 });
2632
2633  
2634
2635  /*
2636  * - LGPL
2637  *
2638  * Pagination item
2639  * 
2640  */
2641
2642
2643 /**
2644  * @class Roo.bootstrap.PaginationItem
2645  * @extends Roo.bootstrap.Component
2646  * Bootstrap PaginationItem class
2647  * @cfg {String} html text
2648  * @cfg {String} href the link
2649  * @cfg {Boolean} preventDefault (true | false) default true
2650  * @cfg {Boolean} active (true | false) default false
2651  * 
2652  * 
2653  * @constructor
2654  * Create a new PaginationItem
2655  * @param {Object} config The config object
2656  */
2657
2658
2659 Roo.bootstrap.PaginationItem = function(config){
2660     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2661     this.addEvents({
2662         // raw events
2663         /**
2664          * @event click
2665          * The raw click event for the entire grid.
2666          * @param {Roo.EventObject} e
2667          */
2668         "click" : true
2669     });
2670 };
2671
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2673     
2674     href : false,
2675     html : false,
2676     preventDefault: true,
2677     active : false,
2678     cls : false,
2679     
2680     getAutoCreate : function(){
2681         var cfg= {
2682             tag: 'li',
2683             cn: [
2684                 {
2685                     tag : 'a',
2686                     href : this.href ? this.href : '#',
2687                     html : this.html ? this.html : ''
2688                 }
2689             ]
2690         };
2691         
2692         if(this.cls){
2693             cfg.cls = this.cls;
2694         }
2695         
2696         if(this.active){
2697             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2698         }
2699         
2700         return cfg;
2701     },
2702     
2703     initEvents: function() {
2704         
2705         this.el.on('click', this.onClick, this);
2706         
2707     },
2708     onClick : function(e)
2709     {
2710         Roo.log('PaginationItem on click ');
2711         if(this.preventDefault){
2712             e.preventDefault();
2713         }
2714         
2715         this.fireEvent('click', this, e);
2716     }
2717    
2718 });
2719
2720  
2721
2722  /*
2723  * - LGPL
2724  *
2725  * slider
2726  * 
2727  */
2728
2729
2730 /**
2731  * @class Roo.bootstrap.Slider
2732  * @extends Roo.bootstrap.Component
2733  * Bootstrap Slider class
2734  *    
2735  * @constructor
2736  * Create a new Slider
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.Slider = function(config){
2741     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2745     
2746     getAutoCreate : function(){
2747         
2748         var cfg = {
2749             tag: 'div',
2750             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2751             cn: [
2752                 {
2753                     tag: 'a',
2754                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2755                 }
2756             ]
2757         }
2758         
2759         return cfg;
2760     }
2761    
2762 });
2763
2764  /*
2765  * - LGPL
2766  *
2767  * table
2768  * 
2769  */
2770
2771 /**
2772  * @class Roo.bootstrap.Table
2773  * @extends Roo.bootstrap.Component
2774  * Bootstrap Table class
2775  * @cfg {String} cls table class
2776  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777  * @cfg {String} bgcolor Specifies the background color for a table
2778  * @cfg {Number} border Specifies whether the table cells should have borders or not
2779  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780  * @cfg {Number} cellspacing Specifies the space between cells
2781  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783  * @cfg {String} sortable Specifies that the table should be sortable
2784  * @cfg {String} summary Specifies a summary of the content of a table
2785  * @cfg {Number} width Specifies the width of a table
2786  * 
2787  * @cfg {boolean} striped Should the rows be alternative striped
2788  * @cfg {boolean} bordered Add borders to the table
2789  * @cfg {boolean} hover Add hover highlighting
2790  * @cfg {boolean} condensed Format condensed
2791  * @cfg {boolean} responsive Format condensed
2792  *
2793  
2794  
2795  * 
2796  * @constructor
2797  * Create a new Table
2798  * @param {Object} config The config object
2799  */
2800
2801 Roo.bootstrap.Table = function(config){
2802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2803     
2804     if (this.sm) {
2805         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806         this.sm = this.selModel;
2807         this.sm.xmodule = this.xmodule || false;
2808     }
2809     if (this.cm && typeof(this.cm.config) == 'undefined') {
2810         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811         this.cm = this.colModel;
2812         this.cm.xmodule = this.xmodule || false;
2813     }
2814     if (this.store) {
2815         this.store= Roo.factory(this.store, Roo.data);
2816         this.ds = this.store;
2817         this.ds.xmodule = this.xmodule || false;
2818          
2819     }
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2823     
2824     cls: false,
2825     align: false,
2826     bgcolor: false,
2827     border: false,
2828     cellpadding: false,
2829     cellspacing: false,
2830     frame: false,
2831     rules: false,
2832     sortable: false,
2833     summary: false,
2834     width: false,
2835     striped : false,
2836     bordered: false,
2837     hover:  false,
2838     condensed : false,
2839     responsive : false,
2840     sm : false,
2841     cm : false,
2842     store : false,
2843     
2844     getAutoCreate : function(){
2845         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2846         
2847         cfg = {
2848             tag: 'table',
2849             cls : 'table',
2850             cn : []
2851         }
2852             
2853         if (this.striped) {
2854             cfg.cls += ' table-striped';
2855         }
2856         if (this.hover) {
2857             cfg.cls += ' table-hover';
2858         }
2859         if (this.bordered) {
2860             cfg.cls += ' table-bordered';
2861         }
2862         if (this.condensed) {
2863             cfg.cls += ' table-condensed';
2864         }
2865         if (this.responsive) {
2866             cfg.cls += ' table-responsive';
2867         }
2868         
2869           
2870         
2871         
2872         if (this.cls) {
2873             cfg.cls+=  ' ' +this.cls;
2874         }
2875         
2876         // this lot should be simplifed...
2877         
2878         if (this.align) {
2879             cfg.align=this.align;
2880         }
2881         if (this.bgcolor) {
2882             cfg.bgcolor=this.bgcolor;
2883         }
2884         if (this.border) {
2885             cfg.border=this.border;
2886         }
2887         if (this.cellpadding) {
2888             cfg.cellpadding=this.cellpadding;
2889         }
2890         if (this.cellspacing) {
2891             cfg.cellspacing=this.cellspacing;
2892         }
2893         if (this.frame) {
2894             cfg.frame=this.frame;
2895         }
2896         if (this.rules) {
2897             cfg.rules=this.rules;
2898         }
2899         if (this.sortable) {
2900             cfg.sortable=this.sortable;
2901         }
2902         if (this.summary) {
2903             cfg.summary=this.summary;
2904         }
2905         if (this.width) {
2906             cfg.width=this.width;
2907         }
2908         
2909         if(this.store || this.cm){
2910             cfg.cn.push(this.renderHeader());
2911             cfg.cn.push(this.renderBody());
2912             cfg.cn.push(this.renderFooter());
2913             
2914             cfg.cls+=  ' TableGrid';
2915         }
2916         
2917         return cfg;
2918     },
2919 //    
2920 //    initTableGrid : function()
2921 //    {
2922 //        var cfg = {};
2923 //        
2924 //        var header = {
2925 //            tag: 'thead',
2926 //            cn : []
2927 //        };
2928 //        
2929 //        var cm = this.cm;
2930 //        
2931 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2932 //            header.cn.push({
2933 //                tag: 'th',
2934 //                html: cm.getColumnHeader(i)
2935 //            })
2936 //        }
2937 //        
2938 //        cfg.push(header);
2939 //        
2940 //        return cfg;
2941 //        
2942 //        
2943 //    },
2944     
2945     initEvents : function()
2946     {   
2947         if(!this.store || !this.cm){
2948             return;
2949         }
2950         
2951         Roo.log('initEvents with ds!!!!');
2952         
2953 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2954 //        this.maskEl.enableDisplayMode("block");
2955 //        this.maskEl.show();
2956         
2957         this.store.on('load', this.onLoad, this);
2958         this.store.on('beforeload', this.onBeforeLoad, this);
2959         
2960         this.store.load();
2961         
2962         
2963         
2964     },
2965     
2966     renderHeader : function()
2967     {
2968         var header = {
2969             tag: 'thead',
2970             cn : []
2971         };
2972         
2973         var cm = this.cm;
2974         
2975         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2976             header.cn.push({
2977                 tag: 'th',
2978                 html: cm.getColumnHeader(i)
2979             })
2980         }
2981         
2982         return header;
2983     },
2984     
2985     renderBody : function()
2986     {
2987         var body = {
2988             tag: 'tbody',
2989             cn : []
2990         };
2991         
2992         return body;
2993     },
2994     
2995     renderFooter : function()
2996     {
2997         var footer = {
2998             tag: 'tfoot',
2999             cn : []
3000         };
3001         
3002         return footer;
3003     },
3004     
3005     onLoad : function()
3006     {
3007         Roo.log('ds onload');
3008         
3009         var cm = this.cm;
3010         
3011         var tbody = this.el.select('tbody', true).first();
3012         
3013         var renders = [];
3014         
3015         if(this.store.getCount() > 0){
3016             this.store.data.each(function(d){
3017                 var row = {
3018                     tag : 'tr',
3019                     cn : []
3020                 };
3021                 
3022                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3023                     var renderer = cm.getRenderer(i);
3024                     Roo.log('config!!!!!!!!!!!!!!!!!!!');
3025                     Roo.log(cm.config(i));
3026                     var value = '';
3027                     var id = Roo.id();
3028                     
3029                     if(typeof(renderer) !== 'undefined'){
3030                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3031                     }
3032                     
3033                     if(typeof(value) === 'object'){
3034                         renders.push({
3035                             id : id,
3036                             cfg : value 
3037                         })
3038                     }
3039                     
3040                     row.cn.push({
3041                         tag: 'td',
3042                         id: id,
3043                         html: (typeof(value) === 'object') ? '' : value
3044                     })
3045                    
3046                 }
3047                 
3048                 tbody.createChild(row);
3049                 
3050             });
3051         }
3052         
3053         
3054         if(renders.length){
3055             var _this = this;
3056             Roo.each(renders, function(r){
3057                 _this.renderColumn(r);
3058             })
3059         }
3060 //        
3061 //        if(this.loadMask){
3062 //            this.maskEl.hide();
3063 //        }
3064     },
3065     
3066     onBeforeLoad : function()
3067     {
3068         Roo.log('ds onBeforeLoad');
3069         
3070         this.clear();
3071         
3072 //        if(this.loadMask){
3073 //            this.maskEl.show();
3074 //        }
3075     },
3076     
3077     clear : function()
3078     {
3079         this.el.select('tbody', true).first().dom.innerHTML = '';
3080     },
3081     
3082     getSelectionModel : function(){
3083         if(!this.selModel){
3084             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3085         }
3086         return this.selModel;
3087     },
3088     
3089     renderColumn : function(r)
3090     {
3091         var _this = this;
3092         r.cfg.render(Roo.get(r.id));
3093         
3094         if(r.cfg.cn){
3095             Roo.each(r.cfg.cn, function(c){
3096                 var child = {
3097                     id: r.id,
3098                     cfg: c
3099                 }
3100                 _this.renderColumn(child);
3101             })
3102         }
3103     }
3104    
3105 });
3106
3107  
3108
3109  /*
3110  * - LGPL
3111  *
3112  * table cell
3113  * 
3114  */
3115
3116 /**
3117  * @class Roo.bootstrap.TableCell
3118  * @extends Roo.bootstrap.Component
3119  * Bootstrap TableCell class
3120  * @cfg {String} html cell contain text
3121  * @cfg {String} cls cell class
3122  * @cfg {String} tag cell tag (td|th) default td
3123  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3124  * @cfg {String} align Aligns the content in a cell
3125  * @cfg {String} axis Categorizes cells
3126  * @cfg {String} bgcolor Specifies the background color of a cell
3127  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3128  * @cfg {Number} colspan Specifies the number of columns a cell should span
3129  * @cfg {String} headers Specifies one or more header cells a cell is related to
3130  * @cfg {Number} height Sets the height of a cell
3131  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3132  * @cfg {Number} rowspan Sets the number of rows a cell should span
3133  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3134  * @cfg {String} valign Vertical aligns the content in a cell
3135  * @cfg {Number} width Specifies the width of a cell
3136  * 
3137  * @constructor
3138  * Create a new TableCell
3139  * @param {Object} config The config object
3140  */
3141
3142 Roo.bootstrap.TableCell = function(config){
3143     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3144 };
3145
3146 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3147     
3148     html: false,
3149     cls: false,
3150     tag: false,
3151     abbr: false,
3152     align: false,
3153     axis: false,
3154     bgcolor: false,
3155     charoff: false,
3156     colspan: false,
3157     headers: false,
3158     height: false,
3159     nowrap: false,
3160     rowspan: false,
3161     scope: false,
3162     valign: false,
3163     width: false,
3164     
3165     
3166     getAutoCreate : function(){
3167         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3168         
3169         cfg = {
3170             tag: 'td'
3171         }
3172         
3173         if(this.tag){
3174             cfg.tag = this.tag;
3175         }
3176         
3177         if (this.html) {
3178             cfg.html=this.html
3179         }
3180         if (this.cls) {
3181             cfg.cls=this.cls
3182         }
3183         if (this.abbr) {
3184             cfg.abbr=this.abbr
3185         }
3186         if (this.align) {
3187             cfg.align=this.align
3188         }
3189         if (this.axis) {
3190             cfg.axis=this.axis
3191         }
3192         if (this.bgcolor) {
3193             cfg.bgcolor=this.bgcolor
3194         }
3195         if (this.charoff) {
3196             cfg.charoff=this.charoff
3197         }
3198         if (this.colspan) {
3199             cfg.colspan=this.colspan
3200         }
3201         if (this.headers) {
3202             cfg.headers=this.headers
3203         }
3204         if (this.height) {
3205             cfg.height=this.height
3206         }
3207         if (this.nowrap) {
3208             cfg.nowrap=this.nowrap
3209         }
3210         if (this.rowspan) {
3211             cfg.rowspan=this.rowspan
3212         }
3213         if (this.scope) {
3214             cfg.scope=this.scope
3215         }
3216         if (this.valign) {
3217             cfg.valign=this.valign
3218         }
3219         if (this.width) {
3220             cfg.width=this.width
3221         }
3222         
3223         
3224         return cfg;
3225     }
3226    
3227 });
3228
3229  
3230
3231  /*
3232  * - LGPL
3233  *
3234  * table row
3235  * 
3236  */
3237
3238 /**
3239  * @class Roo.bootstrap.TableRow
3240  * @extends Roo.bootstrap.Component
3241  * Bootstrap TableRow class
3242  * @cfg {String} cls row class
3243  * @cfg {String} align Aligns the content in a table row
3244  * @cfg {String} bgcolor Specifies a background color for a table row
3245  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3246  * @cfg {String} valign Vertical aligns the content in a table row
3247  * 
3248  * @constructor
3249  * Create a new TableRow
3250  * @param {Object} config The config object
3251  */
3252
3253 Roo.bootstrap.TableRow = function(config){
3254     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3255 };
3256
3257 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3258     
3259     cls: false,
3260     align: false,
3261     bgcolor: false,
3262     charoff: false,
3263     valign: false,
3264     
3265     getAutoCreate : function(){
3266         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3267         
3268         cfg = {
3269             tag: 'tr'
3270         }
3271             
3272         if(this.cls){
3273             cfg.cls = this.cls;
3274         }
3275         if(this.align){
3276             cfg.align = this.align;
3277         }
3278         if(this.bgcolor){
3279             cfg.bgcolor = this.bgcolor;
3280         }
3281         if(this.charoff){
3282             cfg.charoff = this.charoff;
3283         }
3284         if(this.valign){
3285             cfg.valign = this.valign;
3286         }
3287         
3288         return cfg;
3289     }
3290    
3291 });
3292
3293  
3294
3295  /*
3296  * - LGPL
3297  *
3298  * table body
3299  * 
3300  */
3301
3302 /**
3303  * @class Roo.bootstrap.TableBody
3304  * @extends Roo.bootstrap.Component
3305  * Bootstrap TableBody class
3306  * @cfg {String} cls element class
3307  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3308  * @cfg {String} align Aligns the content inside the element
3309  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3310  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3311  * 
3312  * @constructor
3313  * Create a new TableBody
3314  * @param {Object} config The config object
3315  */
3316
3317 Roo.bootstrap.TableBody = function(config){
3318     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3319 };
3320
3321 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3322     
3323     cls: false,
3324     tag: false,
3325     align: false,
3326     charoff: false,
3327     valign: false,
3328     
3329     getAutoCreate : function(){
3330         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3331         
3332         cfg = {
3333             tag: 'tbody'
3334         }
3335             
3336         if (this.cls) {
3337             cfg.cls=this.cls
3338         }
3339         if(this.tag){
3340             cfg.tag = this.tag;
3341         }
3342         
3343         if(this.align){
3344             cfg.align = this.align;
3345         }
3346         if(this.charoff){
3347             cfg.charoff = this.charoff;
3348         }
3349         if(this.valign){
3350             cfg.valign = this.valign;
3351         }
3352         
3353         return cfg;
3354     }
3355     
3356     
3357 //    initEvents : function()
3358 //    {
3359 //        
3360 //        if(!this.store){
3361 //            return;
3362 //        }
3363 //        
3364 //        this.store = Roo.factory(this.store, Roo.data);
3365 //        this.store.on('load', this.onLoad, this);
3366 //        
3367 //        this.store.load();
3368 //        
3369 //    },
3370 //    
3371 //    onLoad: function () 
3372 //    {   
3373 //        this.fireEvent('load', this);
3374 //    }
3375 //    
3376 //   
3377 });
3378
3379  
3380
3381  /*
3382  * Based on:
3383  * Ext JS Library 1.1.1
3384  * Copyright(c) 2006-2007, Ext JS, LLC.
3385  *
3386  * Originally Released Under LGPL - original licence link has changed is not relivant.
3387  *
3388  * Fork - LGPL
3389  * <script type="text/javascript">
3390  */
3391
3392 // as we use this in bootstrap.
3393 Roo.namespace('Roo.form');
3394  /**
3395  * @class Roo.form.Action
3396  * Internal Class used to handle form actions
3397  * @constructor
3398  * @param {Roo.form.BasicForm} el The form element or its id
3399  * @param {Object} config Configuration options
3400  */
3401
3402  
3403  
3404 // define the action interface
3405 Roo.form.Action = function(form, options){
3406     this.form = form;
3407     this.options = options || {};
3408 };
3409 /**
3410  * Client Validation Failed
3411  * @const 
3412  */
3413 Roo.form.Action.CLIENT_INVALID = 'client';
3414 /**
3415  * Server Validation Failed
3416  * @const 
3417  */
3418 Roo.form.Action.SERVER_INVALID = 'server';
3419  /**
3420  * Connect to Server Failed
3421  * @const 
3422  */
3423 Roo.form.Action.CONNECT_FAILURE = 'connect';
3424 /**
3425  * Reading Data from Server Failed
3426  * @const 
3427  */
3428 Roo.form.Action.LOAD_FAILURE = 'load';
3429
3430 Roo.form.Action.prototype = {
3431     type : 'default',
3432     failureType : undefined,
3433     response : undefined,
3434     result : undefined,
3435
3436     // interface method
3437     run : function(options){
3438
3439     },
3440
3441     // interface method
3442     success : function(response){
3443
3444     },
3445
3446     // interface method
3447     handleResponse : function(response){
3448
3449     },
3450
3451     // default connection failure
3452     failure : function(response){
3453         
3454         this.response = response;
3455         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3456         this.form.afterAction(this, false);
3457     },
3458
3459     processResponse : function(response){
3460         this.response = response;
3461         if(!response.responseText){
3462             return true;
3463         }
3464         this.result = this.handleResponse(response);
3465         return this.result;
3466     },
3467
3468     // utility functions used internally
3469     getUrl : function(appendParams){
3470         var url = this.options.url || this.form.url || this.form.el.dom.action;
3471         if(appendParams){
3472             var p = this.getParams();
3473             if(p){
3474                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3475             }
3476         }
3477         return url;
3478     },
3479
3480     getMethod : function(){
3481         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3482     },
3483
3484     getParams : function(){
3485         var bp = this.form.baseParams;
3486         var p = this.options.params;
3487         if(p){
3488             if(typeof p == "object"){
3489                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3490             }else if(typeof p == 'string' && bp){
3491                 p += '&' + Roo.urlEncode(bp);
3492             }
3493         }else if(bp){
3494             p = Roo.urlEncode(bp);
3495         }
3496         return p;
3497     },
3498
3499     createCallback : function(){
3500         return {
3501             success: this.success,
3502             failure: this.failure,
3503             scope: this,
3504             timeout: (this.form.timeout*1000),
3505             upload: this.form.fileUpload ? this.success : undefined
3506         };
3507     }
3508 };
3509
3510 Roo.form.Action.Submit = function(form, options){
3511     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3512 };
3513
3514 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3515     type : 'submit',
3516
3517     haveProgress : false,
3518     uploadComplete : false,
3519     
3520     // uploadProgress indicator.
3521     uploadProgress : function()
3522     {
3523         if (!this.form.progressUrl) {
3524             return;
3525         }
3526         
3527         if (!this.haveProgress) {
3528             Roo.MessageBox.progress("Uploading", "Uploading");
3529         }
3530         if (this.uploadComplete) {
3531            Roo.MessageBox.hide();
3532            return;
3533         }
3534         
3535         this.haveProgress = true;
3536    
3537         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3538         
3539         var c = new Roo.data.Connection();
3540         c.request({
3541             url : this.form.progressUrl,
3542             params: {
3543                 id : uid
3544             },
3545             method: 'GET',
3546             success : function(req){
3547                //console.log(data);
3548                 var rdata = false;
3549                 var edata;
3550                 try  {
3551                    rdata = Roo.decode(req.responseText)
3552                 } catch (e) {
3553                     Roo.log("Invalid data from server..");
3554                     Roo.log(edata);
3555                     return;
3556                 }
3557                 if (!rdata || !rdata.success) {
3558                     Roo.log(rdata);
3559                     Roo.MessageBox.alert(Roo.encode(rdata));
3560                     return;
3561                 }
3562                 var data = rdata.data;
3563                 
3564                 if (this.uploadComplete) {
3565                    Roo.MessageBox.hide();
3566                    return;
3567                 }
3568                    
3569                 if (data){
3570                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3571                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3572                     );
3573                 }
3574                 this.uploadProgress.defer(2000,this);
3575             },
3576        
3577             failure: function(data) {
3578                 Roo.log('progress url failed ');
3579                 Roo.log(data);
3580             },
3581             scope : this
3582         });
3583            
3584     },
3585     
3586     
3587     run : function()
3588     {
3589         // run get Values on the form, so it syncs any secondary forms.
3590         this.form.getValues();
3591         
3592         var o = this.options;
3593         var method = this.getMethod();
3594         var isPost = method == 'POST';
3595         if(o.clientValidation === false || this.form.isValid()){
3596             
3597             if (this.form.progressUrl) {
3598                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3599                     (new Date() * 1) + '' + Math.random());
3600                     
3601             } 
3602             
3603             
3604             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3605                 form:this.form.el.dom,
3606                 url:this.getUrl(!isPost),
3607                 method: method,
3608                 params:isPost ? this.getParams() : null,
3609                 isUpload: this.form.fileUpload
3610             }));
3611             
3612             this.uploadProgress();
3613
3614         }else if (o.clientValidation !== false){ // client validation failed
3615             this.failureType = Roo.form.Action.CLIENT_INVALID;
3616             this.form.afterAction(this, false);
3617         }
3618     },
3619
3620     success : function(response)
3621     {
3622         this.uploadComplete= true;
3623         if (this.haveProgress) {
3624             Roo.MessageBox.hide();
3625         }
3626         
3627         
3628         var result = this.processResponse(response);
3629         if(result === true || result.success){
3630             this.form.afterAction(this, true);
3631             return;
3632         }
3633         if(result.errors){
3634             this.form.markInvalid(result.errors);
3635             this.failureType = Roo.form.Action.SERVER_INVALID;
3636         }
3637         this.form.afterAction(this, false);
3638     },
3639     failure : function(response)
3640     {
3641         this.uploadComplete= true;
3642         if (this.haveProgress) {
3643             Roo.MessageBox.hide();
3644         }
3645         
3646         this.response = response;
3647         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3648         this.form.afterAction(this, false);
3649     },
3650     
3651     handleResponse : function(response){
3652         if(this.form.errorReader){
3653             var rs = this.form.errorReader.read(response);
3654             var errors = [];
3655             if(rs.records){
3656                 for(var i = 0, len = rs.records.length; i < len; i++) {
3657                     var r = rs.records[i];
3658                     errors[i] = r.data;
3659                 }
3660             }
3661             if(errors.length < 1){
3662                 errors = null;
3663             }
3664             return {
3665                 success : rs.success,
3666                 errors : errors
3667             };
3668         }
3669         var ret = false;
3670         try {
3671             ret = Roo.decode(response.responseText);
3672         } catch (e) {
3673             ret = {
3674                 success: false,
3675                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3676                 errors : []
3677             };
3678         }
3679         return ret;
3680         
3681     }
3682 });
3683
3684
3685 Roo.form.Action.Load = function(form, options){
3686     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3687     this.reader = this.form.reader;
3688 };
3689
3690 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3691     type : 'load',
3692
3693     run : function(){
3694         
3695         Roo.Ajax.request(Roo.apply(
3696                 this.createCallback(), {
3697                     method:this.getMethod(),
3698                     url:this.getUrl(false),
3699                     params:this.getParams()
3700         }));
3701     },
3702
3703     success : function(response){
3704         
3705         var result = this.processResponse(response);
3706         if(result === true || !result.success || !result.data){
3707             this.failureType = Roo.form.Action.LOAD_FAILURE;
3708             this.form.afterAction(this, false);
3709             return;
3710         }
3711         this.form.clearInvalid();
3712         this.form.setValues(result.data);
3713         this.form.afterAction(this, true);
3714     },
3715
3716     handleResponse : function(response){
3717         if(this.form.reader){
3718             var rs = this.form.reader.read(response);
3719             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3720             return {
3721                 success : rs.success,
3722                 data : data
3723             };
3724         }
3725         return Roo.decode(response.responseText);
3726     }
3727 });
3728
3729 Roo.form.Action.ACTION_TYPES = {
3730     'load' : Roo.form.Action.Load,
3731     'submit' : Roo.form.Action.Submit
3732 };/*
3733  * - LGPL
3734  *
3735  * form
3736  * 
3737  */
3738
3739 /**
3740  * @class Roo.bootstrap.Form
3741  * @extends Roo.bootstrap.Component
3742  * Bootstrap Form class
3743  * @cfg {String} method  GET | POST (default POST)
3744  * @cfg {String} labelAlign top | left (default top)
3745   * @cfg {String} align left  | right - for navbars
3746
3747  * 
3748  * @constructor
3749  * Create a new Form
3750  * @param {Object} config The config object
3751  */
3752
3753
3754 Roo.bootstrap.Form = function(config){
3755     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3756     this.addEvents({
3757         /**
3758          * @event clientvalidation
3759          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3760          * @param {Form} this
3761          * @param {Boolean} valid true if the form has passed client-side validation
3762          */
3763         clientvalidation: true,
3764         /**
3765          * @event beforeaction
3766          * Fires before any action is performed. Return false to cancel the action.
3767          * @param {Form} this
3768          * @param {Action} action The action to be performed
3769          */
3770         beforeaction: true,
3771         /**
3772          * @event actionfailed
3773          * Fires when an action fails.
3774          * @param {Form} this
3775          * @param {Action} action The action that failed
3776          */
3777         actionfailed : true,
3778         /**
3779          * @event actioncomplete
3780          * Fires when an action is completed.
3781          * @param {Form} this
3782          * @param {Action} action The action that completed
3783          */
3784         actioncomplete : true
3785     });
3786     
3787 };
3788
3789 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3790       
3791      /**
3792      * @cfg {String} method
3793      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3794      */
3795     method : 'POST',
3796     /**
3797      * @cfg {String} url
3798      * The URL to use for form actions if one isn't supplied in the action options.
3799      */
3800     /**
3801      * @cfg {Boolean} fileUpload
3802      * Set to true if this form is a file upload.
3803      */
3804      
3805     /**
3806      * @cfg {Object} baseParams
3807      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3808      */
3809       
3810     /**
3811      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3812      */
3813     timeout: 30,
3814     /**
3815      * @cfg {Sting} align (left|right) for navbar forms
3816      */
3817     align : 'left',
3818
3819     // private
3820     activeAction : null,
3821  
3822     /**
3823      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3824      * element by passing it or its id or mask the form itself by passing in true.
3825      * @type Mixed
3826      */
3827     waitMsgTarget : false,
3828     
3829      
3830     
3831     /**
3832      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3833      * element by passing it or its id or mask the form itself by passing in true.
3834      * @type Mixed
3835      */
3836     
3837     getAutoCreate : function(){
3838         
3839         var cfg = {
3840             tag: 'form',
3841             method : this.method || 'POST',
3842             id : this.id || Roo.id(),
3843             cls : ''
3844         }
3845         if (this.parent().xtype.match(/^Nav/)) {
3846             cfg.cls = 'navbar-form navbar-' + this.align;
3847             
3848         }
3849         
3850         if (this.labelAlign == 'left' ) {
3851             cfg.cls += ' form-horizontal';
3852         }
3853         
3854         
3855         return cfg;
3856     },
3857     initEvents : function()
3858     {
3859         this.el.on('submit', this.onSubmit, this);
3860         
3861         
3862     },
3863     // private
3864     onSubmit : function(e){
3865         e.stopEvent();
3866     },
3867     
3868      /**
3869      * Returns true if client-side validation on the form is successful.
3870      * @return Boolean
3871      */
3872     isValid : function(){
3873         var items = this.getItems();
3874         var valid = true;
3875         items.each(function(f){
3876            if(!f.validate()){
3877                valid = false;
3878                
3879            }
3880         });
3881         return valid;
3882     },
3883     /**
3884      * Returns true if any fields in this form have changed since their original load.
3885      * @return Boolean
3886      */
3887     isDirty : function(){
3888         var dirty = false;
3889         var items = this.getItems();
3890         items.each(function(f){
3891            if(f.isDirty()){
3892                dirty = true;
3893                return false;
3894            }
3895            return true;
3896         });
3897         return dirty;
3898     },
3899      /**
3900      * Performs a predefined action (submit or load) or custom actions you define on this form.
3901      * @param {String} actionName The name of the action type
3902      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3903      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3904      * accept other config options):
3905      * <pre>
3906 Property          Type             Description
3907 ----------------  ---------------  ----------------------------------------------------------------------------------
3908 url               String           The url for the action (defaults to the form's url)
3909 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3910 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3911 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3912                                    validate the form on the client (defaults to false)
3913      * </pre>
3914      * @return {BasicForm} this
3915      */
3916     doAction : function(action, options){
3917         if(typeof action == 'string'){
3918             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3919         }
3920         if(this.fireEvent('beforeaction', this, action) !== false){
3921             this.beforeAction(action);
3922             action.run.defer(100, action);
3923         }
3924         return this;
3925     },
3926     
3927     // private
3928     beforeAction : function(action){
3929         var o = action.options;
3930         
3931         // not really supported yet.. ??
3932         
3933         //if(this.waitMsgTarget === true){
3934             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3935         //}else if(this.waitMsgTarget){
3936         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3937         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3938         //}else {
3939         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3940        // }
3941          
3942     },
3943
3944     // private
3945     afterAction : function(action, success){
3946         this.activeAction = null;
3947         var o = action.options;
3948         
3949         //if(this.waitMsgTarget === true){
3950             this.el.unmask();
3951         //}else if(this.waitMsgTarget){
3952         //    this.waitMsgTarget.unmask();
3953         //}else{
3954         //    Roo.MessageBox.updateProgress(1);
3955         //    Roo.MessageBox.hide();
3956        // }
3957         // 
3958         if(success){
3959             if(o.reset){
3960                 this.reset();
3961             }
3962             Roo.callback(o.success, o.scope, [this, action]);
3963             this.fireEvent('actioncomplete', this, action);
3964             
3965         }else{
3966             
3967             // failure condition..
3968             // we have a scenario where updates need confirming.
3969             // eg. if a locking scenario exists..
3970             // we look for { errors : { needs_confirm : true }} in the response.
3971             if (
3972                 (typeof(action.result) != 'undefined')  &&
3973                 (typeof(action.result.errors) != 'undefined')  &&
3974                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3975            ){
3976                 var _t = this;
3977                 Roo.log("not supported yet");
3978                  /*
3979                 
3980                 Roo.MessageBox.confirm(
3981                     "Change requires confirmation",
3982                     action.result.errorMsg,
3983                     function(r) {
3984                         if (r != 'yes') {
3985                             return;
3986                         }
3987                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
3988                     }
3989                     
3990                 );
3991                 */
3992                 
3993                 
3994                 return;
3995             }
3996             
3997             Roo.callback(o.failure, o.scope, [this, action]);
3998             // show an error message if no failed handler is set..
3999             if (!this.hasListener('actionfailed')) {
4000                 Roo.log("need to add dialog support");
4001                 /*
4002                 Roo.MessageBox.alert("Error",
4003                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4004                         action.result.errorMsg :
4005                         "Saving Failed, please check your entries or try again"
4006                 );
4007                 */
4008             }
4009             
4010             this.fireEvent('actionfailed', this, action);
4011         }
4012         
4013     },
4014     /**
4015      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4016      * @param {String} id The value to search for
4017      * @return Field
4018      */
4019     findField : function(id){
4020         var items = this.getItems();
4021         var field = items.get(id);
4022         if(!field){
4023              items.each(function(f){
4024                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4025                     field = f;
4026                     return false;
4027                 }
4028                 return true;
4029             });
4030         }
4031         return field || null;
4032     },
4033      /**
4034      * Mark fields in this form invalid in bulk.
4035      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4036      * @return {BasicForm} this
4037      */
4038     markInvalid : function(errors){
4039         if(errors instanceof Array){
4040             for(var i = 0, len = errors.length; i < len; i++){
4041                 var fieldError = errors[i];
4042                 var f = this.findField(fieldError.id);
4043                 if(f){
4044                     f.markInvalid(fieldError.msg);
4045                 }
4046             }
4047         }else{
4048             var field, id;
4049             for(id in errors){
4050                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4051                     field.markInvalid(errors[id]);
4052                 }
4053             }
4054         }
4055         //Roo.each(this.childForms || [], function (f) {
4056         //    f.markInvalid(errors);
4057         //});
4058         
4059         return this;
4060     },
4061
4062     /**
4063      * Set values for fields in this form in bulk.
4064      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4065      * @return {BasicForm} this
4066      */
4067     setValues : function(values){
4068         if(values instanceof Array){ // array of objects
4069             for(var i = 0, len = values.length; i < len; i++){
4070                 var v = values[i];
4071                 var f = this.findField(v.id);
4072                 if(f){
4073                     f.setValue(v.value);
4074                     if(this.trackResetOnLoad){
4075                         f.originalValue = f.getValue();
4076                     }
4077                 }
4078             }
4079         }else{ // object hash
4080             var field, id;
4081             for(id in values){
4082                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4083                     
4084                     if (field.setFromData && 
4085                         field.valueField && 
4086                         field.displayField &&
4087                         // combos' with local stores can 
4088                         // be queried via setValue()
4089                         // to set their value..
4090                         (field.store && !field.store.isLocal)
4091                         ) {
4092                         // it's a combo
4093                         var sd = { };
4094                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4095                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4096                         field.setFromData(sd);
4097                         
4098                     } else {
4099                         field.setValue(values[id]);
4100                     }
4101                     
4102                     
4103                     if(this.trackResetOnLoad){
4104                         field.originalValue = field.getValue();
4105                     }
4106                 }
4107             }
4108         }
4109          
4110         //Roo.each(this.childForms || [], function (f) {
4111         //    f.setValues(values);
4112         //});
4113                 
4114         return this;
4115     },
4116
4117     /**
4118      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4119      * they are returned as an array.
4120      * @param {Boolean} asString
4121      * @return {Object}
4122      */
4123     getValues : function(asString){
4124         //if (this.childForms) {
4125             // copy values from the child forms
4126         //    Roo.each(this.childForms, function (f) {
4127         //        this.setValues(f.getValues());
4128         //    }, this);
4129         //}
4130         
4131         
4132         
4133         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4134         if(asString === true){
4135             return fs;
4136         }
4137         return Roo.urlDecode(fs);
4138     },
4139     
4140     /**
4141      * Returns the fields in this form as an object with key/value pairs. 
4142      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4143      * @return {Object}
4144      */
4145     getFieldValues : function(with_hidden)
4146     {
4147         var items = this.getItems();
4148         var ret = {};
4149         items.each(function(f){
4150             if (!f.getName()) {
4151                 return;
4152             }
4153             var v = f.getValue();
4154             if (f.inputType =='radio') {
4155                 if (typeof(ret[f.getName()]) == 'undefined') {
4156                     ret[f.getName()] = ''; // empty..
4157                 }
4158                 
4159                 if (!f.el.dom.checked) {
4160                     return;
4161                     
4162                 }
4163                 v = f.el.dom.value;
4164                 
4165             }
4166             
4167             // not sure if this supported any more..
4168             if ((typeof(v) == 'object') && f.getRawValue) {
4169                 v = f.getRawValue() ; // dates..
4170             }
4171             // combo boxes where name != hiddenName...
4172             if (f.name != f.getName()) {
4173                 ret[f.name] = f.getRawValue();
4174             }
4175             ret[f.getName()] = v;
4176         });
4177         
4178         return ret;
4179     },
4180
4181     /**
4182      * Clears all invalid messages in this form.
4183      * @return {BasicForm} this
4184      */
4185     clearInvalid : function(){
4186         var items = this.getItems();
4187         
4188         items.each(function(f){
4189            f.clearInvalid();
4190         });
4191         
4192         
4193         
4194         return this;
4195     },
4196
4197     /**
4198      * Resets this form.
4199      * @return {BasicForm} this
4200      */
4201     reset : function(){
4202         var items = this.getItems();
4203         items.each(function(f){
4204             f.reset();
4205         });
4206         
4207         Roo.each(this.childForms || [], function (f) {
4208             f.reset();
4209         });
4210        
4211         
4212         return this;
4213     },
4214     getItems : function()
4215     {
4216         var r=new Roo.util.MixedCollection(false, function(o){
4217             return o.id || (o.id = Roo.id());
4218         });
4219         var iter = function(el) {
4220             if (el.inputEl) {
4221                 r.add(el);
4222             }
4223             if (!el.items) {
4224                 return;
4225             }
4226             Roo.each(el.items,function(e) {
4227                 iter(e);
4228             });
4229             
4230             
4231         };
4232         iter(this);
4233         return r;
4234         
4235         
4236         
4237         
4238     }
4239     
4240 });
4241
4242  
4243 /*
4244  * Based on:
4245  * Ext JS Library 1.1.1
4246  * Copyright(c) 2006-2007, Ext JS, LLC.
4247  *
4248  * Originally Released Under LGPL - original licence link has changed is not relivant.
4249  *
4250  * Fork - LGPL
4251  * <script type="text/javascript">
4252  */
4253 /**
4254  * @class Roo.form.VTypes
4255  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4256  * @singleton
4257  */
4258 Roo.form.VTypes = function(){
4259     // closure these in so they are only created once.
4260     var alpha = /^[a-zA-Z_]+$/;
4261     var alphanum = /^[a-zA-Z0-9_]+$/;
4262     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4263     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4264
4265     // All these messages and functions are configurable
4266     return {
4267         /**
4268          * The function used to validate email addresses
4269          * @param {String} value The email address
4270          */
4271         'email' : function(v){
4272             return email.test(v);
4273         },
4274         /**
4275          * The error text to display when the email validation function returns false
4276          * @type String
4277          */
4278         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4279         /**
4280          * The keystroke filter mask to be applied on email input
4281          * @type RegExp
4282          */
4283         'emailMask' : /[a-z0-9_\.\-@]/i,
4284
4285         /**
4286          * The function used to validate URLs
4287          * @param {String} value The URL
4288          */
4289         'url' : function(v){
4290             return url.test(v);
4291         },
4292         /**
4293          * The error text to display when the url validation function returns false
4294          * @type String
4295          */
4296         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4297         
4298         /**
4299          * The function used to validate alpha values
4300          * @param {String} value The value
4301          */
4302         'alpha' : function(v){
4303             return alpha.test(v);
4304         },
4305         /**
4306          * The error text to display when the alpha validation function returns false
4307          * @type String
4308          */
4309         'alphaText' : 'This field should only contain letters and _',
4310         /**
4311          * The keystroke filter mask to be applied on alpha input
4312          * @type RegExp
4313          */
4314         'alphaMask' : /[a-z_]/i,
4315
4316         /**
4317          * The function used to validate alphanumeric values
4318          * @param {String} value The value
4319          */
4320         'alphanum' : function(v){
4321             return alphanum.test(v);
4322         },
4323         /**
4324          * The error text to display when the alphanumeric validation function returns false
4325          * @type String
4326          */
4327         'alphanumText' : 'This field should only contain letters, numbers and _',
4328         /**
4329          * The keystroke filter mask to be applied on alphanumeric input
4330          * @type RegExp
4331          */
4332         'alphanumMask' : /[a-z0-9_]/i
4333     };
4334 }();/*
4335  * - LGPL
4336  *
4337  * Input
4338  * 
4339  */
4340
4341 /**
4342  * @class Roo.bootstrap.Input
4343  * @extends Roo.bootstrap.Component
4344  * Bootstrap Input class
4345  * @cfg {Boolean} disabled is it disabled
4346  * @cfg {String} fieldLabel - the label associated
4347  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4348  * @cfg {String} name name of the input
4349  * @cfg {string} fieldLabel - the label associated
4350  * @cfg {string}  inputType - input / file submit ...
4351  * @cfg {string} placeholder - placeholder to put in text.
4352  * @cfg {string}  before - input group add on before
4353  * @cfg {string} after - input group add on after
4354  * @cfg {string} size - (lg|sm) or leave empty..
4355  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4356  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4357  * @cfg {Number} md colspan out of 12 for computer-sized screens
4358  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4359  * @cfg {string} value default value of the input
4360  * @cfg {Number} labelWidth set the width of label (0-12)
4361  * @cfg {String} labelAlign (top|left)
4362  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4363  * 
4364  * 
4365  * @constructor
4366  * Create a new Input
4367  * @param {Object} config The config object
4368  */
4369
4370 Roo.bootstrap.Input = function(config){
4371     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4372    
4373         this.addEvents({
4374             /**
4375              * @event focus
4376              * Fires when this field receives input focus.
4377              * @param {Roo.form.Field} this
4378              */
4379             focus : true,
4380             /**
4381              * @event blur
4382              * Fires when this field loses input focus.
4383              * @param {Roo.form.Field} this
4384              */
4385             blur : true,
4386             /**
4387              * @event specialkey
4388              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4389              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4390              * @param {Roo.form.Field} this
4391              * @param {Roo.EventObject} e The event object
4392              */
4393             specialkey : true,
4394             /**
4395              * @event change
4396              * Fires just before the field blurs if the field value has changed.
4397              * @param {Roo.form.Field} this
4398              * @param {Mixed} newValue The new value
4399              * @param {Mixed} oldValue The original value
4400              */
4401             change : true,
4402             /**
4403              * @event invalid
4404              * Fires after the field has been marked as invalid.
4405              * @param {Roo.form.Field} this
4406              * @param {String} msg The validation message
4407              */
4408             invalid : true,
4409             /**
4410              * @event valid
4411              * Fires after the field has been validated with no errors.
4412              * @param {Roo.form.Field} this
4413              */
4414             valid : true,
4415              /**
4416              * @event keyup
4417              * Fires after the key up
4418              * @param {Roo.form.Field} this
4419              * @param {Roo.EventObject}  e The event Object
4420              */
4421             keyup : true
4422         });
4423 };
4424
4425 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4426      /**
4427      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4428       automatic validation (defaults to "keyup").
4429      */
4430     validationEvent : "keyup",
4431      /**
4432      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4433      */
4434     validateOnBlur : true,
4435     /**
4436      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4437      */
4438     validationDelay : 250,
4439      /**
4440      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4441      */
4442     focusClass : "x-form-focus",  // not needed???
4443     
4444        
4445     /**
4446      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4447      */
4448     invalidClass : "has-error",
4449     
4450     /**
4451      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4452      */
4453     selectOnFocus : false,
4454     
4455      /**
4456      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4457      */
4458     maskRe : null,
4459        /**
4460      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4461      */
4462     vtype : null,
4463     
4464       /**
4465      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4466      */
4467     disableKeyFilter : false,
4468     
4469        /**
4470      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4471      */
4472     disabled : false,
4473      /**
4474      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4475      */
4476     allowBlank : true,
4477     /**
4478      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4479      */
4480     blankText : "This field is required",
4481     
4482      /**
4483      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4484      */
4485     minLength : 0,
4486     /**
4487      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4488      */
4489     maxLength : Number.MAX_VALUE,
4490     /**
4491      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4492      */
4493     minLengthText : "The minimum length for this field is {0}",
4494     /**
4495      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4496      */
4497     maxLengthText : "The maximum length for this field is {0}",
4498   
4499     
4500     /**
4501      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4502      * If available, this function will be called only after the basic validators all return true, and will be passed the
4503      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4504      */
4505     validator : null,
4506     /**
4507      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4508      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4509      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4510      */
4511     regex : null,
4512     /**
4513      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4514      */
4515     regexText : "",
4516     
4517     
4518     
4519     fieldLabel : '',
4520     inputType : 'text',
4521     
4522     name : false,
4523     placeholder: false,
4524     before : false,
4525     after : false,
4526     size : false,
4527     // private
4528     hasFocus : false,
4529     preventMark: false,
4530     isFormField : true,
4531     value : '',
4532     labelWidth : 2,
4533     labelAlign : false,
4534     readOnly : false,
4535     
4536     parentLabelAlign : function()
4537     {
4538         var parent = this;
4539         while (parent.parent()) {
4540             parent = parent.parent();
4541             if (typeof(parent.labelAlign) !='undefined') {
4542                 return parent.labelAlign;
4543             }
4544         }
4545         return 'left';
4546         
4547     },
4548     
4549     getAutoCreate : function(){
4550         
4551         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4552         
4553         var id = Roo.id();
4554         
4555         var cfg = {};
4556         
4557         if(this.inputType != 'hidden'){
4558             cfg.cls = 'form-group' //input-group
4559         }
4560         
4561         var input =  {
4562             tag: 'input',
4563             id : id,
4564             type : this.inputType,
4565             value : this.value,
4566             cls : 'form-control',
4567             placeholder : this.placeholder || ''
4568             
4569         };
4570         
4571         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4572             input.maxLength = this.maxLength;
4573         }
4574         
4575         if (this.disabled) {
4576             input.disabled=true;
4577         }
4578         
4579         if (this.readOnly) {
4580             input.readonly=true;
4581         }
4582         
4583         if (this.name) {
4584             input.name = this.name;
4585         }
4586         if (this.size) {
4587             input.cls += ' input-' + this.size;
4588         }
4589         var settings=this;
4590         ['xs','sm','md','lg'].map(function(size){
4591             if (settings[size]) {
4592                 cfg.cls += ' col-' + size + '-' + settings[size];
4593             }
4594         });
4595         
4596         var inputblock = input;
4597         
4598         if (this.before || this.after) {
4599             
4600             inputblock = {
4601                 cls : 'input-group',
4602                 cn :  [] 
4603             };
4604             if (this.before) {
4605                 inputblock.cn.push({
4606                     tag :'span',
4607                     cls : 'input-group-addon',
4608                     html : this.before
4609                 });
4610             }
4611             inputblock.cn.push(input);
4612             if (this.after) {
4613                 inputblock.cn.push({
4614                     tag :'span',
4615                     cls : 'input-group-addon',
4616                     html : this.after
4617                 });
4618             }
4619             
4620         };
4621         
4622         if (align ==='left' && this.fieldLabel.length) {
4623                 Roo.log("left and has label");
4624                 cfg.cn = [
4625                     
4626                     {
4627                         tag: 'label',
4628                         'for' :  id,
4629                         cls : 'control-label col-sm-' + this.labelWidth,
4630                         html : this.fieldLabel
4631                         
4632                     },
4633                     {
4634                         cls : "col-sm-" + (12 - this.labelWidth), 
4635                         cn: [
4636                             inputblock
4637                         ]
4638                     }
4639                     
4640                 ];
4641         } else if ( this.fieldLabel.length) {
4642                 Roo.log(" label");
4643                  cfg.cn = [
4644                    
4645                     {
4646                         tag: 'label',
4647                         //cls : 'input-group-addon',
4648                         html : this.fieldLabel
4649                         
4650                     },
4651                     
4652                     inputblock
4653                     
4654                 ];
4655
4656         } else {
4657             
4658                 Roo.log(" no label && no align");
4659                 cfg.cn = [
4660                     
4661                         inputblock
4662                     
4663                 ];
4664                 
4665                 
4666         };
4667         Roo.log('input-parentType: ' + this.parentType);
4668         
4669         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4670            cfg.cls += ' navbar-form';
4671            Roo.log(cfg);
4672         }
4673         
4674         return cfg;
4675         
4676     },
4677     /**
4678      * return the real input element.
4679      */
4680     inputEl: function ()
4681     {
4682         return this.el.select('input.form-control',true).first();
4683     },
4684     setDisabled : function(v)
4685     {
4686         var i  = this.inputEl().dom;
4687         if (!v) {
4688             i.removeAttribute('disabled');
4689             return;
4690             
4691         }
4692         i.setAttribute('disabled','true');
4693     },
4694     initEvents : function()
4695     {
4696         
4697         this.inputEl().on("keydown" , this.fireKey,  this);
4698         this.inputEl().on("focus", this.onFocus,  this);
4699         this.inputEl().on("blur", this.onBlur,  this);
4700         
4701         this.inputEl().relayEvent('keyup', this);
4702
4703         // reference to original value for reset
4704         this.originalValue = this.getValue();
4705         //Roo.form.TextField.superclass.initEvents.call(this);
4706         if(this.validationEvent == 'keyup'){
4707             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4708             this.inputEl().on('keyup', this.filterValidation, this);
4709         }
4710         else if(this.validationEvent !== false){
4711             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4712         }
4713         
4714         if(this.selectOnFocus){
4715             this.on("focus", this.preFocus, this);
4716             
4717         }
4718         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4719             this.inputEl().on("keypress", this.filterKeys, this);
4720         }
4721        /* if(this.grow){
4722             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4723             this.el.on("click", this.autoSize,  this);
4724         }
4725         */
4726         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4727             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4728         }
4729         
4730     },
4731     filterValidation : function(e){
4732         if(!e.isNavKeyPress()){
4733             this.validationTask.delay(this.validationDelay);
4734         }
4735     },
4736      /**
4737      * Validates the field value
4738      * @return {Boolean} True if the value is valid, else false
4739      */
4740     validate : function(){
4741         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4742         if(this.disabled || this.validateValue(this.getRawValue())){
4743             this.clearInvalid();
4744             return true;
4745         }
4746         return false;
4747     },
4748     
4749     
4750     /**
4751      * Validates a value according to the field's validation rules and marks the field as invalid
4752      * if the validation fails
4753      * @param {Mixed} value The value to validate
4754      * @return {Boolean} True if the value is valid, else false
4755      */
4756     validateValue : function(value){
4757         if(value.length < 1)  { // if it's blank
4758              if(this.allowBlank){
4759                 this.clearInvalid();
4760                 return true;
4761              }else{
4762                 this.markInvalid(this.blankText);
4763                 return false;
4764              }
4765         }
4766         if(value.length < this.minLength){
4767             this.markInvalid(String.format(this.minLengthText, this.minLength));
4768             return false;
4769         }
4770         if(value.length > this.maxLength){
4771             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4772             return false;
4773         }
4774         if(this.vtype){
4775             var vt = Roo.form.VTypes;
4776             if(!vt[this.vtype](value, this)){
4777                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4778                 return false;
4779             }
4780         }
4781         if(typeof this.validator == "function"){
4782             var msg = this.validator(value);
4783             if(msg !== true){
4784                 this.markInvalid(msg);
4785                 return false;
4786             }
4787         }
4788         if(this.regex && !this.regex.test(value)){
4789             this.markInvalid(this.regexText);
4790             return false;
4791         }
4792         return true;
4793     },
4794
4795     
4796     
4797      // private
4798     fireKey : function(e){
4799         //Roo.log('field ' + e.getKey());
4800         if(e.isNavKeyPress()){
4801             this.fireEvent("specialkey", this, e);
4802         }
4803     },
4804     focus : function (selectText){
4805         if(this.rendered){
4806             this.inputEl().focus();
4807             if(selectText === true){
4808                 this.inputEl().dom.select();
4809             }
4810         }
4811         return this;
4812     } ,
4813     
4814     onFocus : function(){
4815         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4816            // this.el.addClass(this.focusClass);
4817         }
4818         if(!this.hasFocus){
4819             this.hasFocus = true;
4820             this.startValue = this.getValue();
4821             this.fireEvent("focus", this);
4822         }
4823     },
4824     
4825     beforeBlur : Roo.emptyFn,
4826
4827     
4828     // private
4829     onBlur : function(){
4830         this.beforeBlur();
4831         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4832             //this.el.removeClass(this.focusClass);
4833         }
4834         this.hasFocus = false;
4835         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4836             this.validate();
4837         }
4838         var v = this.getValue();
4839         if(String(v) !== String(this.startValue)){
4840             this.fireEvent('change', this, v, this.startValue);
4841         }
4842         this.fireEvent("blur", this);
4843     },
4844     
4845     /**
4846      * Resets the current field value to the originally loaded value and clears any validation messages
4847      */
4848     reset : function(){
4849         this.setValue(this.originalValue);
4850         this.clearInvalid();
4851     },
4852      /**
4853      * Returns the name of the field
4854      * @return {Mixed} name The name field
4855      */
4856     getName: function(){
4857         return this.name;
4858     },
4859      /**
4860      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4861      * @return {Mixed} value The field value
4862      */
4863     getValue : function(){
4864         return this.inputEl().getValue();
4865     },
4866     /**
4867      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4868      * @return {Mixed} value The field value
4869      */
4870     getRawValue : function(){
4871         var v = this.inputEl().getValue();
4872         
4873         return v;
4874     },
4875     
4876     /**
4877      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4878      * @param {Mixed} value The value to set
4879      */
4880     setRawValue : function(v){
4881         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4882     },
4883     
4884     selectText : function(start, end){
4885         var v = this.getRawValue();
4886         if(v.length > 0){
4887             start = start === undefined ? 0 : start;
4888             end = end === undefined ? v.length : end;
4889             var d = this.inputEl().dom;
4890             if(d.setSelectionRange){
4891                 d.setSelectionRange(start, end);
4892             }else if(d.createTextRange){
4893                 var range = d.createTextRange();
4894                 range.moveStart("character", start);
4895                 range.moveEnd("character", v.length-end);
4896                 range.select();
4897             }
4898         }
4899     },
4900     
4901     /**
4902      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4903      * @param {Mixed} value The value to set
4904      */
4905     setValue : function(v){
4906         this.value = v;
4907         if(this.rendered){
4908             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4909             this.validate();
4910         }
4911     },
4912     
4913     /*
4914     processValue : function(value){
4915         if(this.stripCharsRe){
4916             var newValue = value.replace(this.stripCharsRe, '');
4917             if(newValue !== value){
4918                 this.setRawValue(newValue);
4919                 return newValue;
4920             }
4921         }
4922         return value;
4923     },
4924   */
4925     preFocus : function(){
4926         
4927         if(this.selectOnFocus){
4928             this.inputEl().dom.select();
4929         }
4930     },
4931     filterKeys : function(e){
4932         var k = e.getKey();
4933         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4934             return;
4935         }
4936         var c = e.getCharCode(), cc = String.fromCharCode(c);
4937         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4938             return;
4939         }
4940         if(!this.maskRe.test(cc)){
4941             e.stopEvent();
4942         }
4943     },
4944      /**
4945      * Clear any invalid styles/messages for this field
4946      */
4947     clearInvalid : function(){
4948         
4949         if(!this.el || this.preventMark){ // not rendered
4950             return;
4951         }
4952         this.el.removeClass(this.invalidClass);
4953         /*
4954         switch(this.msgTarget){
4955             case 'qtip':
4956                 this.el.dom.qtip = '';
4957                 break;
4958             case 'title':
4959                 this.el.dom.title = '';
4960                 break;
4961             case 'under':
4962                 if(this.errorEl){
4963                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4964                 }
4965                 break;
4966             case 'side':
4967                 if(this.errorIcon){
4968                     this.errorIcon.dom.qtip = '';
4969                     this.errorIcon.hide();
4970                     this.un('resize', this.alignErrorIcon, this);
4971                 }
4972                 break;
4973             default:
4974                 var t = Roo.getDom(this.msgTarget);
4975                 t.innerHTML = '';
4976                 t.style.display = 'none';
4977                 break;
4978         }
4979         */
4980         this.fireEvent('valid', this);
4981     },
4982      /**
4983      * Mark this field as invalid
4984      * @param {String} msg The validation message
4985      */
4986     markInvalid : function(msg){
4987         if(!this.el  || this.preventMark){ // not rendered
4988             return;
4989         }
4990         this.el.addClass(this.invalidClass);
4991         /*
4992         msg = msg || this.invalidText;
4993         switch(this.msgTarget){
4994             case 'qtip':
4995                 this.el.dom.qtip = msg;
4996                 this.el.dom.qclass = 'x-form-invalid-tip';
4997                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
4998                     Roo.QuickTips.enable();
4999                 }
5000                 break;
5001             case 'title':
5002                 this.el.dom.title = msg;
5003                 break;
5004             case 'under':
5005                 if(!this.errorEl){
5006                     var elp = this.el.findParent('.x-form-element', 5, true);
5007                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5008                     this.errorEl.setWidth(elp.getWidth(true)-20);
5009                 }
5010                 this.errorEl.update(msg);
5011                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5012                 break;
5013             case 'side':
5014                 if(!this.errorIcon){
5015                     var elp = this.el.findParent('.x-form-element', 5, true);
5016                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5017                 }
5018                 this.alignErrorIcon();
5019                 this.errorIcon.dom.qtip = msg;
5020                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5021                 this.errorIcon.show();
5022                 this.on('resize', this.alignErrorIcon, this);
5023                 break;
5024             default:
5025                 var t = Roo.getDom(this.msgTarget);
5026                 t.innerHTML = msg;
5027                 t.style.display = this.msgDisplay;
5028                 break;
5029         }
5030         */
5031         this.fireEvent('invalid', this, msg);
5032     },
5033     // private
5034     SafariOnKeyDown : function(event)
5035     {
5036         // this is a workaround for a password hang bug on chrome/ webkit.
5037         
5038         var isSelectAll = false;
5039         
5040         if(this.inputEl().dom.selectionEnd > 0){
5041             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5042         }
5043         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5044             event.preventDefault();
5045             this.setValue('');
5046             return;
5047         }
5048         
5049         if(isSelectAll){ // backspace and delete key
5050             
5051             event.preventDefault();
5052             // this is very hacky as keydown always get's upper case.
5053             //
5054             var cc = String.fromCharCode(event.getCharCode());
5055             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5056             
5057         }
5058     },
5059     adjustWidth : function(tag, w){
5060         tag = tag.toLowerCase();
5061         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5062             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5063                 if(tag == 'input'){
5064                     return w + 2;
5065                 }
5066                 if(tag == 'textarea'){
5067                     return w-2;
5068                 }
5069             }else if(Roo.isOpera){
5070                 if(tag == 'input'){
5071                     return w + 2;
5072                 }
5073                 if(tag == 'textarea'){
5074                     return w-2;
5075                 }
5076             }
5077         }
5078         return w;
5079     }
5080     
5081 });
5082
5083  
5084 /*
5085  * - LGPL
5086  *
5087  * Input
5088  * 
5089  */
5090
5091 /**
5092  * @class Roo.bootstrap.TextArea
5093  * @extends Roo.bootstrap.Input
5094  * Bootstrap TextArea class
5095  * @cfg {Number} cols Specifies the visible width of a text area
5096  * @cfg {Number} rows Specifies the visible number of lines in a text area
5097  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5098  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5099  * @cfg {string} html text
5100  * 
5101  * @constructor
5102  * Create a new TextArea
5103  * @param {Object} config The config object
5104  */
5105
5106 Roo.bootstrap.TextArea = function(config){
5107     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5108    
5109 };
5110
5111 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5112      
5113     cols : false,
5114     rows : 5,
5115     readOnly : false,
5116     warp : 'soft',
5117     resize : false,
5118     value: false,
5119     html: false,
5120     
5121     getAutoCreate : function(){
5122         
5123         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5124         
5125         var id = Roo.id();
5126         
5127         var cfg = {};
5128         
5129         var input =  {
5130             tag: 'textarea',
5131             id : id,
5132             warp : this.warp,
5133             rows : this.rows,
5134             value : this.value || '',
5135             html: this.html || '',
5136             cls : 'form-control',
5137             placeholder : this.placeholder || '' 
5138             
5139         };
5140         
5141         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5142             input.maxLength = this.maxLength;
5143         }
5144         
5145         if(this.resize){
5146             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5147         }
5148         
5149         if(this.cols){
5150             input.cols = this.cols;
5151         }
5152         
5153         if (this.readOnly) {
5154             input.readonly = true;
5155         }
5156         
5157         if (this.name) {
5158             input.name = this.name;
5159         }
5160         
5161         if (this.size) {
5162             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5163         }
5164         
5165         var settings=this;
5166         ['xs','sm','md','lg'].map(function(size){
5167             if (settings[size]) {
5168                 cfg.cls += ' col-' + size + '-' + settings[size];
5169             }
5170         });
5171         
5172         var inputblock = input;
5173         
5174         if (this.before || this.after) {
5175             
5176             inputblock = {
5177                 cls : 'input-group',
5178                 cn :  [] 
5179             };
5180             if (this.before) {
5181                 inputblock.cn.push({
5182                     tag :'span',
5183                     cls : 'input-group-addon',
5184                     html : this.before
5185                 });
5186             }
5187             inputblock.cn.push(input);
5188             if (this.after) {
5189                 inputblock.cn.push({
5190                     tag :'span',
5191                     cls : 'input-group-addon',
5192                     html : this.after
5193                 });
5194             }
5195             
5196         }
5197         
5198         if (align ==='left' && this.fieldLabel.length) {
5199                 Roo.log("left and has label");
5200                 cfg.cn = [
5201                     
5202                     {
5203                         tag: 'label',
5204                         'for' :  id,
5205                         cls : 'control-label col-sm-' + this.labelWidth,
5206                         html : this.fieldLabel
5207                         
5208                     },
5209                     {
5210                         cls : "col-sm-" + (12 - this.labelWidth), 
5211                         cn: [
5212                             inputblock
5213                         ]
5214                     }
5215                     
5216                 ];
5217         } else if ( this.fieldLabel.length) {
5218                 Roo.log(" label");
5219                  cfg.cn = [
5220                    
5221                     {
5222                         tag: 'label',
5223                         //cls : 'input-group-addon',
5224                         html : this.fieldLabel
5225                         
5226                     },
5227                     
5228                     inputblock
5229                     
5230                 ];
5231
5232         } else {
5233             
5234                    Roo.log(" no label && no align");
5235                 cfg.cn = [
5236                     
5237                         inputblock
5238                     
5239                 ];
5240                 
5241                 
5242         }
5243         
5244         if (this.disabled) {
5245             input.disabled=true;
5246         }
5247         
5248         return cfg;
5249         
5250     },
5251     /**
5252      * return the real textarea element.
5253      */
5254     inputEl: function ()
5255     {
5256         return this.el.select('textarea.form-control',true).first();
5257     }
5258 });
5259
5260  
5261 /*
5262  * - LGPL
5263  *
5264  * trigger field - base class for combo..
5265  * 
5266  */
5267  
5268 /**
5269  * @class Roo.bootstrap.TriggerField
5270  * @extends Roo.bootstrap.Input
5271  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5272  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5273  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5274  * for which you can provide a custom implementation.  For example:
5275  * <pre><code>
5276 var trigger = new Roo.bootstrap.TriggerField();
5277 trigger.onTriggerClick = myTriggerFn;
5278 trigger.applyTo('my-field');
5279 </code></pre>
5280  *
5281  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5282  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5283  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5284  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5285  * @constructor
5286  * Create a new TriggerField.
5287  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5288  * to the base TextField)
5289  */
5290 Roo.bootstrap.TriggerField = function(config){
5291     this.mimicing = false;
5292     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5293 };
5294
5295 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5296     /**
5297      * @cfg {String} triggerClass A CSS class to apply to the trigger
5298      */
5299      /**
5300      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5301      */
5302     hideTrigger:false,
5303
5304     /** @cfg {Boolean} grow @hide */
5305     /** @cfg {Number} growMin @hide */
5306     /** @cfg {Number} growMax @hide */
5307
5308     /**
5309      * @hide 
5310      * @method
5311      */
5312     autoSize: Roo.emptyFn,
5313     // private
5314     monitorTab : true,
5315     // private
5316     deferHeight : true,
5317
5318     
5319     actionMode : 'wrap',
5320     
5321     
5322     
5323     getAutoCreate : function(){
5324        
5325         var parent = this.parent();
5326         
5327         var align = this.parentLabelAlign();
5328         
5329         var id = Roo.id();
5330         
5331         var cfg = {
5332             cls: 'form-group' //input-group
5333         };
5334         
5335         
5336         var input =  {
5337             tag: 'input',
5338             id : id,
5339             type : this.inputType,
5340             cls : 'form-control',
5341             autocomplete: 'off',
5342             placeholder : this.placeholder || '' 
5343             
5344         };
5345         if (this.name) {
5346             input.name = this.name;
5347         }
5348         if (this.size) {
5349             input.cls += ' input-' + this.size;
5350         }
5351         
5352         if (this.disabled) {
5353             input.disabled=true;
5354         }
5355         
5356         var inputblock = input;
5357         
5358         if (this.before || this.after) {
5359             
5360             inputblock = {
5361                 cls : 'input-group',
5362                 cn :  [] 
5363             };
5364             if (this.before) {
5365                 inputblock.cn.push({
5366                     tag :'span',
5367                     cls : 'input-group-addon',
5368                     html : this.before
5369                 });
5370             }
5371             inputblock.cn.push(input);
5372             if (this.after) {
5373                 inputblock.cn.push({
5374                     tag :'span',
5375                     cls : 'input-group-addon',
5376                     html : this.after
5377                 });
5378             }
5379             
5380         };
5381         
5382         var box = {
5383             tag: 'div',
5384             cn: [
5385                 {
5386                     tag: 'input',
5387                     type : 'hidden',
5388                     cls: 'form-hidden-field'
5389                 },
5390                 inputblock
5391             ]
5392             
5393         };
5394         
5395         if(this.multiple){
5396             Roo.log('multiple');
5397             
5398             box = {
5399                 tag: 'div',
5400                 cn: [
5401                     {
5402                         tag: 'input',
5403                         type : 'hidden',
5404                         cls: 'form-hidden-field'
5405                     },
5406                     {
5407                         tag: 'ul',
5408                         cls: 'select2-choices',
5409                         cn:[
5410                             {
5411                                 tag: 'li',
5412                                 cls: 'select2-search-field',
5413                                 cn: [
5414
5415                                     inputblock
5416                                 ]
5417                             }
5418                         ]
5419                     }
5420                 ]
5421             }
5422         };
5423         
5424         var combobox = {
5425             cls: 'select2-container input-group',
5426             cn: [
5427                 box,
5428                 {
5429                     tag: 'ul',
5430                     cls: 'typeahead typeahead-long dropdown-menu',
5431                     style: 'display:none'
5432                 }
5433             ]
5434         };
5435         
5436         if(!this.multiple){
5437             combobox.cn.push({
5438                 tag :'span',
5439                 cls : 'input-group-addon btn dropdown-toggle',
5440                 cn : [
5441                     {
5442                         tag: 'span',
5443                         cls: 'caret'
5444                     },
5445                     {
5446                         tag: 'span',
5447                         cls: 'combobox-clear',
5448                         cn  : [
5449                             {
5450                                 tag : 'i',
5451                                 cls: 'icon-remove'
5452                             }
5453                         ]
5454                     }
5455                 ]
5456
5457             })
5458         }
5459         
5460         if(this.multiple){
5461             combobox.cls += ' select2-container-multi';
5462         }
5463         
5464         if (align ==='left' && this.fieldLabel.length) {
5465             
5466                 Roo.log("left and has label");
5467                 cfg.cn = [
5468                     
5469                     {
5470                         tag: 'label',
5471                         'for' :  id,
5472                         cls : 'control-label col-sm-' + this.labelWidth,
5473                         html : this.fieldLabel
5474                         
5475                     },
5476                     {
5477                         cls : "col-sm-" + (12 - this.labelWidth), 
5478                         cn: [
5479                             combobox
5480                         ]
5481                     }
5482                     
5483                 ];
5484         } else if ( this.fieldLabel.length) {
5485                 Roo.log(" label");
5486                  cfg.cn = [
5487                    
5488                     {
5489                         tag: 'label',
5490                         //cls : 'input-group-addon',
5491                         html : this.fieldLabel
5492                         
5493                     },
5494                     
5495                     combobox
5496                     
5497                 ];
5498
5499         } else {
5500             
5501                 Roo.log(" no label && no align");
5502                 cfg = combobox
5503                      
5504                 
5505         }
5506          
5507         var settings=this;
5508         ['xs','sm','md','lg'].map(function(size){
5509             if (settings[size]) {
5510                 cfg.cls += ' col-' + size + '-' + settings[size];
5511             }
5512         });
5513         
5514         return cfg;
5515         
5516     },
5517     
5518     
5519     
5520     // private
5521     onResize : function(w, h){
5522 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5523 //        if(typeof w == 'number'){
5524 //            var x = w - this.trigger.getWidth();
5525 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5526 //            this.trigger.setStyle('left', x+'px');
5527 //        }
5528     },
5529
5530     // private
5531     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5532
5533     // private
5534     getResizeEl : function(){
5535         return this.inputEl();
5536     },
5537
5538     // private
5539     getPositionEl : function(){
5540         return this.inputEl();
5541     },
5542
5543     // private
5544     alignErrorIcon : function(){
5545         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5546     },
5547
5548     // private
5549     initEvents : function(){
5550         
5551         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5552         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5553         if(!this.multiple){
5554             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5555             if(this.hideTrigger){
5556                 this.trigger.setDisplayed(false);
5557             }
5558             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5559         }
5560         
5561         if(this.multiple){
5562             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5563         }
5564         
5565         //this.trigger.addClassOnOver('x-form-trigger-over');
5566         //this.trigger.addClassOnClick('x-form-trigger-click');
5567         
5568         //if(!this.width){
5569         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5570         //}
5571     },
5572
5573     // private
5574     initTrigger : function(){
5575        
5576     },
5577
5578     // private
5579     onDestroy : function(){
5580         if(this.trigger){
5581             this.trigger.removeAllListeners();
5582           //  this.trigger.remove();
5583         }
5584         //if(this.wrap){
5585         //    this.wrap.remove();
5586         //}
5587         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5588     },
5589
5590     // private
5591     onFocus : function(){
5592         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5593         /*
5594         if(!this.mimicing){
5595             this.wrap.addClass('x-trigger-wrap-focus');
5596             this.mimicing = true;
5597             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5598             if(this.monitorTab){
5599                 this.el.on("keydown", this.checkTab, this);
5600             }
5601         }
5602         */
5603     },
5604
5605     // private
5606     checkTab : function(e){
5607         if(e.getKey() == e.TAB){
5608             this.triggerBlur();
5609         }
5610     },
5611
5612     // private
5613     onBlur : function(){
5614         // do nothing
5615     },
5616
5617     // private
5618     mimicBlur : function(e, t){
5619         /*
5620         if(!this.wrap.contains(t) && this.validateBlur()){
5621             this.triggerBlur();
5622         }
5623         */
5624     },
5625
5626     // private
5627     triggerBlur : function(){
5628         this.mimicing = false;
5629         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5630         if(this.monitorTab){
5631             this.el.un("keydown", this.checkTab, this);
5632         }
5633         //this.wrap.removeClass('x-trigger-wrap-focus');
5634         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5635     },
5636
5637     // private
5638     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5639     validateBlur : function(e, t){
5640         return true;
5641     },
5642
5643     // private
5644     onDisable : function(){
5645         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5646         //if(this.wrap){
5647         //    this.wrap.addClass('x-item-disabled');
5648         //}
5649     },
5650
5651     // private
5652     onEnable : function(){
5653         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5654         //if(this.wrap){
5655         //    this.el.removeClass('x-item-disabled');
5656         //}
5657     },
5658
5659     // private
5660     onShow : function(){
5661         var ae = this.getActionEl();
5662         
5663         if(ae){
5664             ae.dom.style.display = '';
5665             ae.dom.style.visibility = 'visible';
5666         }
5667     },
5668
5669     // private
5670     
5671     onHide : function(){
5672         var ae = this.getActionEl();
5673         ae.dom.style.display = 'none';
5674     },
5675
5676     /**
5677      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5678      * by an implementing function.
5679      * @method
5680      * @param {EventObject} e
5681      */
5682     onTriggerClick : Roo.emptyFn
5683 });
5684  /*
5685  * Based on:
5686  * Ext JS Library 1.1.1
5687  * Copyright(c) 2006-2007, Ext JS, LLC.
5688  *
5689  * Originally Released Under LGPL - original licence link has changed is not relivant.
5690  *
5691  * Fork - LGPL
5692  * <script type="text/javascript">
5693  */
5694
5695
5696 /**
5697  * @class Roo.data.SortTypes
5698  * @singleton
5699  * Defines the default sorting (casting?) comparison functions used when sorting data.
5700  */
5701 Roo.data.SortTypes = {
5702     /**
5703      * Default sort that does nothing
5704      * @param {Mixed} s The value being converted
5705      * @return {Mixed} The comparison value
5706      */
5707     none : function(s){
5708         return s;
5709     },
5710     
5711     /**
5712      * The regular expression used to strip tags
5713      * @type {RegExp}
5714      * @property
5715      */
5716     stripTagsRE : /<\/?[^>]+>/gi,
5717     
5718     /**
5719      * Strips all HTML tags to sort on text only
5720      * @param {Mixed} s The value being converted
5721      * @return {String} The comparison value
5722      */
5723     asText : function(s){
5724         return String(s).replace(this.stripTagsRE, "");
5725     },
5726     
5727     /**
5728      * Strips all HTML tags to sort on text only - Case insensitive
5729      * @param {Mixed} s The value being converted
5730      * @return {String} The comparison value
5731      */
5732     asUCText : function(s){
5733         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5734     },
5735     
5736     /**
5737      * Case insensitive string
5738      * @param {Mixed} s The value being converted
5739      * @return {String} The comparison value
5740      */
5741     asUCString : function(s) {
5742         return String(s).toUpperCase();
5743     },
5744     
5745     /**
5746      * Date sorting
5747      * @param {Mixed} s The value being converted
5748      * @return {Number} The comparison value
5749      */
5750     asDate : function(s) {
5751         if(!s){
5752             return 0;
5753         }
5754         if(s instanceof Date){
5755             return s.getTime();
5756         }
5757         return Date.parse(String(s));
5758     },
5759     
5760     /**
5761      * Float sorting
5762      * @param {Mixed} s The value being converted
5763      * @return {Float} The comparison value
5764      */
5765     asFloat : function(s) {
5766         var val = parseFloat(String(s).replace(/,/g, ""));
5767         if(isNaN(val)) val = 0;
5768         return val;
5769     },
5770     
5771     /**
5772      * Integer sorting
5773      * @param {Mixed} s The value being converted
5774      * @return {Number} The comparison value
5775      */
5776     asInt : function(s) {
5777         var val = parseInt(String(s).replace(/,/g, ""));
5778         if(isNaN(val)) val = 0;
5779         return val;
5780     }
5781 };/*
5782  * Based on:
5783  * Ext JS Library 1.1.1
5784  * Copyright(c) 2006-2007, Ext JS, LLC.
5785  *
5786  * Originally Released Under LGPL - original licence link has changed is not relivant.
5787  *
5788  * Fork - LGPL
5789  * <script type="text/javascript">
5790  */
5791
5792 /**
5793 * @class Roo.data.Record
5794  * Instances of this class encapsulate both record <em>definition</em> information, and record
5795  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5796  * to access Records cached in an {@link Roo.data.Store} object.<br>
5797  * <p>
5798  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5799  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5800  * objects.<br>
5801  * <p>
5802  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5803  * @constructor
5804  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5805  * {@link #create}. The parameters are the same.
5806  * @param {Array} data An associative Array of data values keyed by the field name.
5807  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5808  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5809  * not specified an integer id is generated.
5810  */
5811 Roo.data.Record = function(data, id){
5812     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5813     this.data = data;
5814 };
5815
5816 /**
5817  * Generate a constructor for a specific record layout.
5818  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5819  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5820  * Each field definition object may contain the following properties: <ul>
5821  * <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,
5822  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5823  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5824  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5825  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5826  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5827  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5828  * this may be omitted.</p></li>
5829  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5830  * <ul><li>auto (Default, implies no conversion)</li>
5831  * <li>string</li>
5832  * <li>int</li>
5833  * <li>float</li>
5834  * <li>boolean</li>
5835  * <li>date</li></ul></p></li>
5836  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5837  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5838  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5839  * by the Reader into an object that will be stored in the Record. It is passed the
5840  * following parameters:<ul>
5841  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5842  * </ul></p></li>
5843  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5844  * </ul>
5845  * <br>usage:<br><pre><code>
5846 var TopicRecord = Roo.data.Record.create(
5847     {name: 'title', mapping: 'topic_title'},
5848     {name: 'author', mapping: 'username'},
5849     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5850     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5851     {name: 'lastPoster', mapping: 'user2'},
5852     {name: 'excerpt', mapping: 'post_text'}
5853 );
5854
5855 var myNewRecord = new TopicRecord({
5856     title: 'Do my job please',
5857     author: 'noobie',
5858     totalPosts: 1,
5859     lastPost: new Date(),
5860     lastPoster: 'Animal',
5861     excerpt: 'No way dude!'
5862 });
5863 myStore.add(myNewRecord);
5864 </code></pre>
5865  * @method create
5866  * @static
5867  */
5868 Roo.data.Record.create = function(o){
5869     var f = function(){
5870         f.superclass.constructor.apply(this, arguments);
5871     };
5872     Roo.extend(f, Roo.data.Record);
5873     var p = f.prototype;
5874     p.fields = new Roo.util.MixedCollection(false, function(field){
5875         return field.name;
5876     });
5877     for(var i = 0, len = o.length; i < len; i++){
5878         p.fields.add(new Roo.data.Field(o[i]));
5879     }
5880     f.getField = function(name){
5881         return p.fields.get(name);  
5882     };
5883     return f;
5884 };
5885
5886 Roo.data.Record.AUTO_ID = 1000;
5887 Roo.data.Record.EDIT = 'edit';
5888 Roo.data.Record.REJECT = 'reject';
5889 Roo.data.Record.COMMIT = 'commit';
5890
5891 Roo.data.Record.prototype = {
5892     /**
5893      * Readonly flag - true if this record has been modified.
5894      * @type Boolean
5895      */
5896     dirty : false,
5897     editing : false,
5898     error: null,
5899     modified: null,
5900
5901     // private
5902     join : function(store){
5903         this.store = store;
5904     },
5905
5906     /**
5907      * Set the named field to the specified value.
5908      * @param {String} name The name of the field to set.
5909      * @param {Object} value The value to set the field to.
5910      */
5911     set : function(name, value){
5912         if(this.data[name] == value){
5913             return;
5914         }
5915         this.dirty = true;
5916         if(!this.modified){
5917             this.modified = {};
5918         }
5919         if(typeof this.modified[name] == 'undefined'){
5920             this.modified[name] = this.data[name];
5921         }
5922         this.data[name] = value;
5923         if(!this.editing && this.store){
5924             this.store.afterEdit(this);
5925         }       
5926     },
5927
5928     /**
5929      * Get the value of the named field.
5930      * @param {String} name The name of the field to get the value of.
5931      * @return {Object} The value of the field.
5932      */
5933     get : function(name){
5934         return this.data[name]; 
5935     },
5936
5937     // private
5938     beginEdit : function(){
5939         this.editing = true;
5940         this.modified = {}; 
5941     },
5942
5943     // private
5944     cancelEdit : function(){
5945         this.editing = false;
5946         delete this.modified;
5947     },
5948
5949     // private
5950     endEdit : function(){
5951         this.editing = false;
5952         if(this.dirty && this.store){
5953             this.store.afterEdit(this);
5954         }
5955     },
5956
5957     /**
5958      * Usually called by the {@link Roo.data.Store} which owns the Record.
5959      * Rejects all changes made to the Record since either creation, or the last commit operation.
5960      * Modified fields are reverted to their original values.
5961      * <p>
5962      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5963      * of reject operations.
5964      */
5965     reject : function(){
5966         var m = this.modified;
5967         for(var n in m){
5968             if(typeof m[n] != "function"){
5969                 this.data[n] = m[n];
5970             }
5971         }
5972         this.dirty = false;
5973         delete this.modified;
5974         this.editing = false;
5975         if(this.store){
5976             this.store.afterReject(this);
5977         }
5978     },
5979
5980     /**
5981      * Usually called by the {@link Roo.data.Store} which owns the Record.
5982      * Commits all changes made to the Record since either creation, or the last commit operation.
5983      * <p>
5984      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5985      * of commit operations.
5986      */
5987     commit : function(){
5988         this.dirty = false;
5989         delete this.modified;
5990         this.editing = false;
5991         if(this.store){
5992             this.store.afterCommit(this);
5993         }
5994     },
5995
5996     // private
5997     hasError : function(){
5998         return this.error != null;
5999     },
6000
6001     // private
6002     clearError : function(){
6003         this.error = null;
6004     },
6005
6006     /**
6007      * Creates a copy of this record.
6008      * @param {String} id (optional) A new record id if you don't want to use this record's id
6009      * @return {Record}
6010      */
6011     copy : function(newId) {
6012         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6013     }
6014 };/*
6015  * Based on:
6016  * Ext JS Library 1.1.1
6017  * Copyright(c) 2006-2007, Ext JS, LLC.
6018  *
6019  * Originally Released Under LGPL - original licence link has changed is not relivant.
6020  *
6021  * Fork - LGPL
6022  * <script type="text/javascript">
6023  */
6024
6025
6026
6027 /**
6028  * @class Roo.data.Store
6029  * @extends Roo.util.Observable
6030  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6031  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6032  * <p>
6033  * 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
6034  * has no knowledge of the format of the data returned by the Proxy.<br>
6035  * <p>
6036  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6037  * instances from the data object. These records are cached and made available through accessor functions.
6038  * @constructor
6039  * Creates a new Store.
6040  * @param {Object} config A config object containing the objects needed for the Store to access data,
6041  * and read the data into Records.
6042  */
6043 Roo.data.Store = function(config){
6044     this.data = new Roo.util.MixedCollection(false);
6045     this.data.getKey = function(o){
6046         return o.id;
6047     };
6048     this.baseParams = {};
6049     // private
6050     this.paramNames = {
6051         "start" : "start",
6052         "limit" : "limit",
6053         "sort" : "sort",
6054         "dir" : "dir",
6055         "multisort" : "_multisort"
6056     };
6057
6058     if(config && config.data){
6059         this.inlineData = config.data;
6060         delete config.data;
6061     }
6062
6063     Roo.apply(this, config);
6064     
6065     if(this.reader){ // reader passed
6066         this.reader = Roo.factory(this.reader, Roo.data);
6067         this.reader.xmodule = this.xmodule || false;
6068         if(!this.recordType){
6069             this.recordType = this.reader.recordType;
6070         }
6071         if(this.reader.onMetaChange){
6072             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6073         }
6074     }
6075
6076     if(this.recordType){
6077         this.fields = this.recordType.prototype.fields;
6078     }
6079     this.modified = [];
6080
6081     this.addEvents({
6082         /**
6083          * @event datachanged
6084          * Fires when the data cache has changed, and a widget which is using this Store
6085          * as a Record cache should refresh its view.
6086          * @param {Store} this
6087          */
6088         datachanged : true,
6089         /**
6090          * @event metachange
6091          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6092          * @param {Store} this
6093          * @param {Object} meta The JSON metadata
6094          */
6095         metachange : true,
6096         /**
6097          * @event add
6098          * Fires when Records have been added to the Store
6099          * @param {Store} this
6100          * @param {Roo.data.Record[]} records The array of Records added
6101          * @param {Number} index The index at which the record(s) were added
6102          */
6103         add : true,
6104         /**
6105          * @event remove
6106          * Fires when a Record has been removed from the Store
6107          * @param {Store} this
6108          * @param {Roo.data.Record} record The Record that was removed
6109          * @param {Number} index The index at which the record was removed
6110          */
6111         remove : true,
6112         /**
6113          * @event update
6114          * Fires when a Record has been updated
6115          * @param {Store} this
6116          * @param {Roo.data.Record} record The Record that was updated
6117          * @param {String} operation The update operation being performed.  Value may be one of:
6118          * <pre><code>
6119  Roo.data.Record.EDIT
6120  Roo.data.Record.REJECT
6121  Roo.data.Record.COMMIT
6122          * </code></pre>
6123          */
6124         update : true,
6125         /**
6126          * @event clear
6127          * Fires when the data cache has been cleared.
6128          * @param {Store} this
6129          */
6130         clear : true,
6131         /**
6132          * @event beforeload
6133          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6134          * the load action will be canceled.
6135          * @param {Store} this
6136          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6137          */
6138         beforeload : true,
6139         /**
6140          * @event beforeloadadd
6141          * Fires after a new set of Records has been loaded.
6142          * @param {Store} this
6143          * @param {Roo.data.Record[]} records The Records that were loaded
6144          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6145          */
6146         beforeloadadd : true,
6147         /**
6148          * @event load
6149          * Fires after a new set of Records has been loaded, before they are added to the store.
6150          * @param {Store} this
6151          * @param {Roo.data.Record[]} records The Records that were loaded
6152          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6153          * @params {Object} return from reader
6154          */
6155         load : true,
6156         /**
6157          * @event loadexception
6158          * Fires if an exception occurs in the Proxy during loading.
6159          * Called with the signature of the Proxy's "loadexception" event.
6160          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6161          * 
6162          * @param {Proxy} 
6163          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6164          * @param {Object} load options 
6165          * @param {Object} jsonData from your request (normally this contains the Exception)
6166          */
6167         loadexception : true
6168     });
6169     
6170     if(this.proxy){
6171         this.proxy = Roo.factory(this.proxy, Roo.data);
6172         this.proxy.xmodule = this.xmodule || false;
6173         this.relayEvents(this.proxy,  ["loadexception"]);
6174     }
6175     this.sortToggle = {};
6176     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6177
6178     Roo.data.Store.superclass.constructor.call(this);
6179
6180     if(this.inlineData){
6181         this.loadData(this.inlineData);
6182         delete this.inlineData;
6183     }
6184 };
6185
6186 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6187      /**
6188     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6189     * without a remote query - used by combo/forms at present.
6190     */
6191     
6192     /**
6193     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6194     */
6195     /**
6196     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6197     */
6198     /**
6199     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6200     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6201     */
6202     /**
6203     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6204     * on any HTTP request
6205     */
6206     /**
6207     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6208     */
6209     /**
6210     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6211     */
6212     multiSort: false,
6213     /**
6214     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6215     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6216     */
6217     remoteSort : false,
6218
6219     /**
6220     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6221      * loaded or when a record is removed. (defaults to false).
6222     */
6223     pruneModifiedRecords : false,
6224
6225     // private
6226     lastOptions : null,
6227
6228     /**
6229      * Add Records to the Store and fires the add event.
6230      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6231      */
6232     add : function(records){
6233         records = [].concat(records);
6234         for(var i = 0, len = records.length; i < len; i++){
6235             records[i].join(this);
6236         }
6237         var index = this.data.length;
6238         this.data.addAll(records);
6239         this.fireEvent("add", this, records, index);
6240     },
6241
6242     /**
6243      * Remove a Record from the Store and fires the remove event.
6244      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6245      */
6246     remove : function(record){
6247         var index = this.data.indexOf(record);
6248         this.data.removeAt(index);
6249         if(this.pruneModifiedRecords){
6250             this.modified.remove(record);
6251         }
6252         this.fireEvent("remove", this, record, index);
6253     },
6254
6255     /**
6256      * Remove all Records from the Store and fires the clear event.
6257      */
6258     removeAll : function(){
6259         this.data.clear();
6260         if(this.pruneModifiedRecords){
6261             this.modified = [];
6262         }
6263         this.fireEvent("clear", this);
6264     },
6265
6266     /**
6267      * Inserts Records to the Store at the given index and fires the add event.
6268      * @param {Number} index The start index at which to insert the passed Records.
6269      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6270      */
6271     insert : function(index, records){
6272         records = [].concat(records);
6273         for(var i = 0, len = records.length; i < len; i++){
6274             this.data.insert(index, records[i]);
6275             records[i].join(this);
6276         }
6277         this.fireEvent("add", this, records, index);
6278     },
6279
6280     /**
6281      * Get the index within the cache of the passed Record.
6282      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6283      * @return {Number} The index of the passed Record. Returns -1 if not found.
6284      */
6285     indexOf : function(record){
6286         return this.data.indexOf(record);
6287     },
6288
6289     /**
6290      * Get the index within the cache of the Record with the passed id.
6291      * @param {String} id The id of the Record to find.
6292      * @return {Number} The index of the Record. Returns -1 if not found.
6293      */
6294     indexOfId : function(id){
6295         return this.data.indexOfKey(id);
6296     },
6297
6298     /**
6299      * Get the Record with the specified id.
6300      * @param {String} id The id of the Record to find.
6301      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6302      */
6303     getById : function(id){
6304         return this.data.key(id);
6305     },
6306
6307     /**
6308      * Get the Record at the specified index.
6309      * @param {Number} index The index of the Record to find.
6310      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6311      */
6312     getAt : function(index){
6313         return this.data.itemAt(index);
6314     },
6315
6316     /**
6317      * Returns a range of Records between specified indices.
6318      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6319      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6320      * @return {Roo.data.Record[]} An array of Records
6321      */
6322     getRange : function(start, end){
6323         return this.data.getRange(start, end);
6324     },
6325
6326     // private
6327     storeOptions : function(o){
6328         o = Roo.apply({}, o);
6329         delete o.callback;
6330         delete o.scope;
6331         this.lastOptions = o;
6332     },
6333
6334     /**
6335      * Loads the Record cache from the configured Proxy using the configured Reader.
6336      * <p>
6337      * If using remote paging, then the first load call must specify the <em>start</em>
6338      * and <em>limit</em> properties in the options.params property to establish the initial
6339      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6340      * <p>
6341      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6342      * and this call will return before the new data has been loaded. Perform any post-processing
6343      * in a callback function, or in a "load" event handler.</strong>
6344      * <p>
6345      * @param {Object} options An object containing properties which control loading options:<ul>
6346      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6347      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6348      * passed the following arguments:<ul>
6349      * <li>r : Roo.data.Record[]</li>
6350      * <li>options: Options object from the load call</li>
6351      * <li>success: Boolean success indicator</li></ul></li>
6352      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6353      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6354      * </ul>
6355      */
6356     load : function(options){
6357         options = options || {};
6358         if(this.fireEvent("beforeload", this, options) !== false){
6359             this.storeOptions(options);
6360             var p = Roo.apply(options.params || {}, this.baseParams);
6361             // if meta was not loaded from remote source.. try requesting it.
6362             if (!this.reader.metaFromRemote) {
6363                 p._requestMeta = 1;
6364             }
6365             if(this.sortInfo && this.remoteSort){
6366                 var pn = this.paramNames;
6367                 p[pn["sort"]] = this.sortInfo.field;
6368                 p[pn["dir"]] = this.sortInfo.direction;
6369             }
6370             if (this.multiSort) {
6371                 var pn = this.paramNames;
6372                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6373             }
6374             
6375             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6376         }
6377     },
6378
6379     /**
6380      * Reloads the Record cache from the configured Proxy using the configured Reader and
6381      * the options from the last load operation performed.
6382      * @param {Object} options (optional) An object containing properties which may override the options
6383      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6384      * the most recently used options are reused).
6385      */
6386     reload : function(options){
6387         this.load(Roo.applyIf(options||{}, this.lastOptions));
6388     },
6389
6390     // private
6391     // Called as a callback by the Reader during a load operation.
6392     loadRecords : function(o, options, success){
6393         if(!o || success === false){
6394             if(success !== false){
6395                 this.fireEvent("load", this, [], options, o);
6396             }
6397             if(options.callback){
6398                 options.callback.call(options.scope || this, [], options, false);
6399             }
6400             return;
6401         }
6402         // if data returned failure - throw an exception.
6403         if (o.success === false) {
6404             // show a message if no listener is registered.
6405             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6406                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6407             }
6408             // loadmask wil be hooked into this..
6409             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6410             return;
6411         }
6412         var r = o.records, t = o.totalRecords || r.length;
6413         
6414         this.fireEvent("beforeloadadd", this, r, options, o);
6415         
6416         if(!options || options.add !== true){
6417             if(this.pruneModifiedRecords){
6418                 this.modified = [];
6419             }
6420             for(var i = 0, len = r.length; i < len; i++){
6421                 r[i].join(this);
6422             }
6423             if(this.snapshot){
6424                 this.data = this.snapshot;
6425                 delete this.snapshot;
6426             }
6427             this.data.clear();
6428             this.data.addAll(r);
6429             this.totalLength = t;
6430             this.applySort();
6431             this.fireEvent("datachanged", this);
6432         }else{
6433             this.totalLength = Math.max(t, this.data.length+r.length);
6434             this.add(r);
6435         }
6436         this.fireEvent("load", this, r, options, o);
6437         if(options.callback){
6438             options.callback.call(options.scope || this, r, options, true);
6439         }
6440     },
6441
6442
6443     /**
6444      * Loads data from a passed data block. A Reader which understands the format of the data
6445      * must have been configured in the constructor.
6446      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6447      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6448      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6449      */
6450     loadData : function(o, append){
6451         var r = this.reader.readRecords(o);
6452         this.loadRecords(r, {add: append}, true);
6453     },
6454
6455     /**
6456      * Gets the number of cached records.
6457      * <p>
6458      * <em>If using paging, this may not be the total size of the dataset. If the data object
6459      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6460      * the data set size</em>
6461      */
6462     getCount : function(){
6463         return this.data.length || 0;
6464     },
6465
6466     /**
6467      * Gets the total number of records in the dataset as returned by the server.
6468      * <p>
6469      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6470      * the dataset size</em>
6471      */
6472     getTotalCount : function(){
6473         return this.totalLength || 0;
6474     },
6475
6476     /**
6477      * Returns the sort state of the Store as an object with two properties:
6478      * <pre><code>
6479  field {String} The name of the field by which the Records are sorted
6480  direction {String} The sort order, "ASC" or "DESC"
6481      * </code></pre>
6482      */
6483     getSortState : function(){
6484         return this.sortInfo;
6485     },
6486
6487     // private
6488     applySort : function(){
6489         if(this.sortInfo && !this.remoteSort){
6490             var s = this.sortInfo, f = s.field;
6491             var st = this.fields.get(f).sortType;
6492             var fn = function(r1, r2){
6493                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6494                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6495             };
6496             this.data.sort(s.direction, fn);
6497             if(this.snapshot && this.snapshot != this.data){
6498                 this.snapshot.sort(s.direction, fn);
6499             }
6500         }
6501     },
6502
6503     /**
6504      * Sets the default sort column and order to be used by the next load operation.
6505      * @param {String} fieldName The name of the field to sort by.
6506      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6507      */
6508     setDefaultSort : function(field, dir){
6509         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6510     },
6511
6512     /**
6513      * Sort the Records.
6514      * If remote sorting is used, the sort is performed on the server, and the cache is
6515      * reloaded. If local sorting is used, the cache is sorted internally.
6516      * @param {String} fieldName The name of the field to sort by.
6517      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6518      */
6519     sort : function(fieldName, dir){
6520         var f = this.fields.get(fieldName);
6521         if(!dir){
6522             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6523             
6524             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6525                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6526             }else{
6527                 dir = f.sortDir;
6528             }
6529         }
6530         this.sortToggle[f.name] = dir;
6531         this.sortInfo = {field: f.name, direction: dir};
6532         if(!this.remoteSort){
6533             this.applySort();
6534             this.fireEvent("datachanged", this);
6535         }else{
6536             this.load(this.lastOptions);
6537         }
6538     },
6539
6540     /**
6541      * Calls the specified function for each of the Records in the cache.
6542      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6543      * Returning <em>false</em> aborts and exits the iteration.
6544      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6545      */
6546     each : function(fn, scope){
6547         this.data.each(fn, scope);
6548     },
6549
6550     /**
6551      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6552      * (e.g., during paging).
6553      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6554      */
6555     getModifiedRecords : function(){
6556         return this.modified;
6557     },
6558
6559     // private
6560     createFilterFn : function(property, value, anyMatch){
6561         if(!value.exec){ // not a regex
6562             value = String(value);
6563             if(value.length == 0){
6564                 return false;
6565             }
6566             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6567         }
6568         return function(r){
6569             return value.test(r.data[property]);
6570         };
6571     },
6572
6573     /**
6574      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6575      * @param {String} property A field on your records
6576      * @param {Number} start The record index to start at (defaults to 0)
6577      * @param {Number} end The last record index to include (defaults to length - 1)
6578      * @return {Number} The sum
6579      */
6580     sum : function(property, start, end){
6581         var rs = this.data.items, v = 0;
6582         start = start || 0;
6583         end = (end || end === 0) ? end : rs.length-1;
6584
6585         for(var i = start; i <= end; i++){
6586             v += (rs[i].data[property] || 0);
6587         }
6588         return v;
6589     },
6590
6591     /**
6592      * Filter the records by a specified property.
6593      * @param {String} field A field on your records
6594      * @param {String/RegExp} value Either a string that the field
6595      * should start with or a RegExp to test against the field
6596      * @param {Boolean} anyMatch True to match any part not just the beginning
6597      */
6598     filter : function(property, value, anyMatch){
6599         var fn = this.createFilterFn(property, value, anyMatch);
6600         return fn ? this.filterBy(fn) : this.clearFilter();
6601     },
6602
6603     /**
6604      * Filter by a function. The specified function will be called with each
6605      * record in this data source. If the function returns true the record is included,
6606      * otherwise it is filtered.
6607      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6608      * @param {Object} scope (optional) The scope of the function (defaults to this)
6609      */
6610     filterBy : function(fn, scope){
6611         this.snapshot = this.snapshot || this.data;
6612         this.data = this.queryBy(fn, scope||this);
6613         this.fireEvent("datachanged", this);
6614     },
6615
6616     /**
6617      * Query the records by a specified property.
6618      * @param {String} field A field on your records
6619      * @param {String/RegExp} value Either a string that the field
6620      * should start with or a RegExp to test against the field
6621      * @param {Boolean} anyMatch True to match any part not just the beginning
6622      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6623      */
6624     query : function(property, value, anyMatch){
6625         var fn = this.createFilterFn(property, value, anyMatch);
6626         return fn ? this.queryBy(fn) : this.data.clone();
6627     },
6628
6629     /**
6630      * Query by a function. The specified function will be called with each
6631      * record in this data source. If the function returns true the record is included
6632      * in the results.
6633      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6634      * @param {Object} scope (optional) The scope of the function (defaults to this)
6635       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6636      **/
6637     queryBy : function(fn, scope){
6638         var data = this.snapshot || this.data;
6639         return data.filterBy(fn, scope||this);
6640     },
6641
6642     /**
6643      * Collects unique values for a particular dataIndex from this store.
6644      * @param {String} dataIndex The property to collect
6645      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6646      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6647      * @return {Array} An array of the unique values
6648      **/
6649     collect : function(dataIndex, allowNull, bypassFilter){
6650         var d = (bypassFilter === true && this.snapshot) ?
6651                 this.snapshot.items : this.data.items;
6652         var v, sv, r = [], l = {};
6653         for(var i = 0, len = d.length; i < len; i++){
6654             v = d[i].data[dataIndex];
6655             sv = String(v);
6656             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6657                 l[sv] = true;
6658                 r[r.length] = v;
6659             }
6660         }
6661         return r;
6662     },
6663
6664     /**
6665      * Revert to a view of the Record cache with no filtering applied.
6666      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6667      */
6668     clearFilter : function(suppressEvent){
6669         if(this.snapshot && this.snapshot != this.data){
6670             this.data = this.snapshot;
6671             delete this.snapshot;
6672             if(suppressEvent !== true){
6673                 this.fireEvent("datachanged", this);
6674             }
6675         }
6676     },
6677
6678     // private
6679     afterEdit : function(record){
6680         if(this.modified.indexOf(record) == -1){
6681             this.modified.push(record);
6682         }
6683         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6684     },
6685     
6686     // private
6687     afterReject : function(record){
6688         this.modified.remove(record);
6689         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6690     },
6691
6692     // private
6693     afterCommit : function(record){
6694         this.modified.remove(record);
6695         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6696     },
6697
6698     /**
6699      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6700      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6701      */
6702     commitChanges : function(){
6703         var m = this.modified.slice(0);
6704         this.modified = [];
6705         for(var i = 0, len = m.length; i < len; i++){
6706             m[i].commit();
6707         }
6708     },
6709
6710     /**
6711      * Cancel outstanding changes on all changed records.
6712      */
6713     rejectChanges : function(){
6714         var m = this.modified.slice(0);
6715         this.modified = [];
6716         for(var i = 0, len = m.length; i < len; i++){
6717             m[i].reject();
6718         }
6719     },
6720
6721     onMetaChange : function(meta, rtype, o){
6722         this.recordType = rtype;
6723         this.fields = rtype.prototype.fields;
6724         delete this.snapshot;
6725         this.sortInfo = meta.sortInfo || this.sortInfo;
6726         this.modified = [];
6727         this.fireEvent('metachange', this, this.reader.meta);
6728     },
6729     
6730     moveIndex : function(data, type)
6731     {
6732         var index = this.indexOf(data);
6733         
6734         var newIndex = index + type;
6735         
6736         this.remove(data);
6737         
6738         this.insert(newIndex, data);
6739         
6740     }
6741 });/*
6742  * Based on:
6743  * Ext JS Library 1.1.1
6744  * Copyright(c) 2006-2007, Ext JS, LLC.
6745  *
6746  * Originally Released Under LGPL - original licence link has changed is not relivant.
6747  *
6748  * Fork - LGPL
6749  * <script type="text/javascript">
6750  */
6751
6752 /**
6753  * @class Roo.data.SimpleStore
6754  * @extends Roo.data.Store
6755  * Small helper class to make creating Stores from Array data easier.
6756  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6757  * @cfg {Array} fields An array of field definition objects, or field name strings.
6758  * @cfg {Array} data The multi-dimensional array of data
6759  * @constructor
6760  * @param {Object} config
6761  */
6762 Roo.data.SimpleStore = function(config){
6763     Roo.data.SimpleStore.superclass.constructor.call(this, {
6764         isLocal : true,
6765         reader: new Roo.data.ArrayReader({
6766                 id: config.id
6767             },
6768             Roo.data.Record.create(config.fields)
6769         ),
6770         proxy : new Roo.data.MemoryProxy(config.data)
6771     });
6772     this.load();
6773 };
6774 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6775  * Based on:
6776  * Ext JS Library 1.1.1
6777  * Copyright(c) 2006-2007, Ext JS, LLC.
6778  *
6779  * Originally Released Under LGPL - original licence link has changed is not relivant.
6780  *
6781  * Fork - LGPL
6782  * <script type="text/javascript">
6783  */
6784
6785 /**
6786 /**
6787  * @extends Roo.data.Store
6788  * @class Roo.data.JsonStore
6789  * Small helper class to make creating Stores for JSON data easier. <br/>
6790 <pre><code>
6791 var store = new Roo.data.JsonStore({
6792     url: 'get-images.php',
6793     root: 'images',
6794     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6795 });
6796 </code></pre>
6797  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6798  * JsonReader and HttpProxy (unless inline data is provided).</b>
6799  * @cfg {Array} fields An array of field definition objects, or field name strings.
6800  * @constructor
6801  * @param {Object} config
6802  */
6803 Roo.data.JsonStore = function(c){
6804     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6805         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6806         reader: new Roo.data.JsonReader(c, c.fields)
6807     }));
6808 };
6809 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6810  * Based on:
6811  * Ext JS Library 1.1.1
6812  * Copyright(c) 2006-2007, Ext JS, LLC.
6813  *
6814  * Originally Released Under LGPL - original licence link has changed is not relivant.
6815  *
6816  * Fork - LGPL
6817  * <script type="text/javascript">
6818  */
6819
6820  
6821 Roo.data.Field = function(config){
6822     if(typeof config == "string"){
6823         config = {name: config};
6824     }
6825     Roo.apply(this, config);
6826     
6827     if(!this.type){
6828         this.type = "auto";
6829     }
6830     
6831     var st = Roo.data.SortTypes;
6832     // named sortTypes are supported, here we look them up
6833     if(typeof this.sortType == "string"){
6834         this.sortType = st[this.sortType];
6835     }
6836     
6837     // set default sortType for strings and dates
6838     if(!this.sortType){
6839         switch(this.type){
6840             case "string":
6841                 this.sortType = st.asUCString;
6842                 break;
6843             case "date":
6844                 this.sortType = st.asDate;
6845                 break;
6846             default:
6847                 this.sortType = st.none;
6848         }
6849     }
6850
6851     // define once
6852     var stripRe = /[\$,%]/g;
6853
6854     // prebuilt conversion function for this field, instead of
6855     // switching every time we're reading a value
6856     if(!this.convert){
6857         var cv, dateFormat = this.dateFormat;
6858         switch(this.type){
6859             case "":
6860             case "auto":
6861             case undefined:
6862                 cv = function(v){ return v; };
6863                 break;
6864             case "string":
6865                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6866                 break;
6867             case "int":
6868                 cv = function(v){
6869                     return v !== undefined && v !== null && v !== '' ?
6870                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6871                     };
6872                 break;
6873             case "float":
6874                 cv = function(v){
6875                     return v !== undefined && v !== null && v !== '' ?
6876                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6877                     };
6878                 break;
6879             case "bool":
6880             case "boolean":
6881                 cv = function(v){ return v === true || v === "true" || v == 1; };
6882                 break;
6883             case "date":
6884                 cv = function(v){
6885                     if(!v){
6886                         return '';
6887                     }
6888                     if(v instanceof Date){
6889                         return v;
6890                     }
6891                     if(dateFormat){
6892                         if(dateFormat == "timestamp"){
6893                             return new Date(v*1000);
6894                         }
6895                         return Date.parseDate(v, dateFormat);
6896                     }
6897                     var parsed = Date.parse(v);
6898                     return parsed ? new Date(parsed) : null;
6899                 };
6900              break;
6901             
6902         }
6903         this.convert = cv;
6904     }
6905 };
6906
6907 Roo.data.Field.prototype = {
6908     dateFormat: null,
6909     defaultValue: "",
6910     mapping: null,
6911     sortType : null,
6912     sortDir : "ASC"
6913 };/*
6914  * Based on:
6915  * Ext JS Library 1.1.1
6916  * Copyright(c) 2006-2007, Ext JS, LLC.
6917  *
6918  * Originally Released Under LGPL - original licence link has changed is not relivant.
6919  *
6920  * Fork - LGPL
6921  * <script type="text/javascript">
6922  */
6923  
6924 // Base class for reading structured data from a data source.  This class is intended to be
6925 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6926
6927 /**
6928  * @class Roo.data.DataReader
6929  * Base class for reading structured data from a data source.  This class is intended to be
6930  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6931  */
6932
6933 Roo.data.DataReader = function(meta, recordType){
6934     
6935     this.meta = meta;
6936     
6937     this.recordType = recordType instanceof Array ? 
6938         Roo.data.Record.create(recordType) : recordType;
6939 };
6940
6941 Roo.data.DataReader.prototype = {
6942      /**
6943      * Create an empty record
6944      * @param {Object} data (optional) - overlay some values
6945      * @return {Roo.data.Record} record created.
6946      */
6947     newRow :  function(d) {
6948         var da =  {};
6949         this.recordType.prototype.fields.each(function(c) {
6950             switch( c.type) {
6951                 case 'int' : da[c.name] = 0; break;
6952                 case 'date' : da[c.name] = new Date(); break;
6953                 case 'float' : da[c.name] = 0.0; break;
6954                 case 'boolean' : da[c.name] = false; break;
6955                 default : da[c.name] = ""; break;
6956             }
6957             
6958         });
6959         return new this.recordType(Roo.apply(da, d));
6960     }
6961     
6962 };/*
6963  * Based on:
6964  * Ext JS Library 1.1.1
6965  * Copyright(c) 2006-2007, Ext JS, LLC.
6966  *
6967  * Originally Released Under LGPL - original licence link has changed is not relivant.
6968  *
6969  * Fork - LGPL
6970  * <script type="text/javascript">
6971  */
6972
6973 /**
6974  * @class Roo.data.DataProxy
6975  * @extends Roo.data.Observable
6976  * This class is an abstract base class for implementations which provide retrieval of
6977  * unformatted data objects.<br>
6978  * <p>
6979  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6980  * (of the appropriate type which knows how to parse the data object) to provide a block of
6981  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6982  * <p>
6983  * Custom implementations must implement the load method as described in
6984  * {@link Roo.data.HttpProxy#load}.
6985  */
6986 Roo.data.DataProxy = function(){
6987     this.addEvents({
6988         /**
6989          * @event beforeload
6990          * Fires before a network request is made to retrieve a data object.
6991          * @param {Object} This DataProxy object.
6992          * @param {Object} params The params parameter to the load function.
6993          */
6994         beforeload : true,
6995         /**
6996          * @event load
6997          * Fires before the load method's callback is called.
6998          * @param {Object} This DataProxy object.
6999          * @param {Object} o The data object.
7000          * @param {Object} arg The callback argument object passed to the load function.
7001          */
7002         load : true,
7003         /**
7004          * @event loadexception
7005          * Fires if an Exception occurs during data retrieval.
7006          * @param {Object} This DataProxy object.
7007          * @param {Object} o The data object.
7008          * @param {Object} arg The callback argument object passed to the load function.
7009          * @param {Object} e The Exception.
7010          */
7011         loadexception : true
7012     });
7013     Roo.data.DataProxy.superclass.constructor.call(this);
7014 };
7015
7016 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7017
7018     /**
7019      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7020      */
7021 /*
7022  * Based on:
7023  * Ext JS Library 1.1.1
7024  * Copyright(c) 2006-2007, Ext JS, LLC.
7025  *
7026  * Originally Released Under LGPL - original licence link has changed is not relivant.
7027  *
7028  * Fork - LGPL
7029  * <script type="text/javascript">
7030  */
7031 /**
7032  * @class Roo.data.MemoryProxy
7033  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7034  * to the Reader when its load method is called.
7035  * @constructor
7036  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7037  */
7038 Roo.data.MemoryProxy = function(data){
7039     if (data.data) {
7040         data = data.data;
7041     }
7042     Roo.data.MemoryProxy.superclass.constructor.call(this);
7043     this.data = data;
7044 };
7045
7046 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7047     /**
7048      * Load data from the requested source (in this case an in-memory
7049      * data object passed to the constructor), read the data object into
7050      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7051      * process that block using the passed callback.
7052      * @param {Object} params This parameter is not used by the MemoryProxy class.
7053      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7054      * object into a block of Roo.data.Records.
7055      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7056      * The function must be passed <ul>
7057      * <li>The Record block object</li>
7058      * <li>The "arg" argument from the load function</li>
7059      * <li>A boolean success indicator</li>
7060      * </ul>
7061      * @param {Object} scope The scope in which to call the callback
7062      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7063      */
7064     load : function(params, reader, callback, scope, arg){
7065         params = params || {};
7066         var result;
7067         try {
7068             result = reader.readRecords(this.data);
7069         }catch(e){
7070             this.fireEvent("loadexception", this, arg, null, e);
7071             callback.call(scope, null, arg, false);
7072             return;
7073         }
7074         callback.call(scope, result, arg, true);
7075     },
7076     
7077     // private
7078     update : function(params, records){
7079         
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.HttpProxy
7093  * @extends Roo.data.DataProxy
7094  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7095  * configured to reference a certain URL.<br><br>
7096  * <p>
7097  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7098  * from which the running page was served.<br><br>
7099  * <p>
7100  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7101  * <p>
7102  * Be aware that to enable the browser to parse an XML document, the server must set
7103  * the Content-Type header in the HTTP response to "text/xml".
7104  * @constructor
7105  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7106  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7107  * will be used to make the request.
7108  */
7109 Roo.data.HttpProxy = function(conn){
7110     Roo.data.HttpProxy.superclass.constructor.call(this);
7111     // is conn a conn config or a real conn?
7112     this.conn = conn;
7113     this.useAjax = !conn || !conn.events;
7114   
7115 };
7116
7117 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7118     // thse are take from connection...
7119     
7120     /**
7121      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7122      */
7123     /**
7124      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7125      * extra parameters to each request made by this object. (defaults to undefined)
7126      */
7127     /**
7128      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7129      *  to each request made by this object. (defaults to undefined)
7130      */
7131     /**
7132      * @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)
7133      */
7134     /**
7135      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7136      */
7137      /**
7138      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7139      * @type Boolean
7140      */
7141   
7142
7143     /**
7144      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7145      * @type Boolean
7146      */
7147     /**
7148      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7149      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7150      * a finer-grained basis than the DataProxy events.
7151      */
7152     getConnection : function(){
7153         return this.useAjax ? Roo.Ajax : this.conn;
7154     },
7155
7156     /**
7157      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7158      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7159      * process that block using the passed callback.
7160      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7161      * for the request to the remote server.
7162      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7163      * object into a block of Roo.data.Records.
7164      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7165      * The function must be passed <ul>
7166      * <li>The Record block object</li>
7167      * <li>The "arg" argument from the load function</li>
7168      * <li>A boolean success indicator</li>
7169      * </ul>
7170      * @param {Object} scope The scope in which to call the callback
7171      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7172      */
7173     load : function(params, reader, callback, scope, arg){
7174         if(this.fireEvent("beforeload", this, params) !== false){
7175             var  o = {
7176                 params : params || {},
7177                 request: {
7178                     callback : callback,
7179                     scope : scope,
7180                     arg : arg
7181                 },
7182                 reader: reader,
7183                 callback : this.loadResponse,
7184                 scope: this
7185             };
7186             if(this.useAjax){
7187                 Roo.applyIf(o, this.conn);
7188                 if(this.activeRequest){
7189                     Roo.Ajax.abort(this.activeRequest);
7190                 }
7191                 this.activeRequest = Roo.Ajax.request(o);
7192             }else{
7193                 this.conn.request(o);
7194             }
7195         }else{
7196             callback.call(scope||this, null, arg, false);
7197         }
7198     },
7199
7200     // private
7201     loadResponse : function(o, success, response){
7202         delete this.activeRequest;
7203         if(!success){
7204             this.fireEvent("loadexception", this, o, response);
7205             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7206             return;
7207         }
7208         var result;
7209         try {
7210             result = o.reader.read(response);
7211         }catch(e){
7212             this.fireEvent("loadexception", this, o, response, e);
7213             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7214             return;
7215         }
7216         
7217         this.fireEvent("load", this, o, o.request.arg);
7218         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7219     },
7220
7221     // private
7222     update : function(dataSet){
7223
7224     },
7225
7226     // private
7227     updateResponse : function(dataSet){
7228
7229     }
7230 });/*
7231  * Based on:
7232  * Ext JS Library 1.1.1
7233  * Copyright(c) 2006-2007, Ext JS, LLC.
7234  *
7235  * Originally Released Under LGPL - original licence link has changed is not relivant.
7236  *
7237  * Fork - LGPL
7238  * <script type="text/javascript">
7239  */
7240
7241 /**
7242  * @class Roo.data.ScriptTagProxy
7243  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7244  * other than the originating domain of the running page.<br><br>
7245  * <p>
7246  * <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
7247  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7248  * <p>
7249  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7250  * source code that is used as the source inside a &lt;script> tag.<br><br>
7251  * <p>
7252  * In order for the browser to process the returned data, the server must wrap the data object
7253  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7254  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7255  * depending on whether the callback name was passed:
7256  * <p>
7257  * <pre><code>
7258 boolean scriptTag = false;
7259 String cb = request.getParameter("callback");
7260 if (cb != null) {
7261     scriptTag = true;
7262     response.setContentType("text/javascript");
7263 } else {
7264     response.setContentType("application/x-json");
7265 }
7266 Writer out = response.getWriter();
7267 if (scriptTag) {
7268     out.write(cb + "(");
7269 }
7270 out.print(dataBlock.toJsonString());
7271 if (scriptTag) {
7272     out.write(");");
7273 }
7274 </pre></code>
7275  *
7276  * @constructor
7277  * @param {Object} config A configuration object.
7278  */
7279 Roo.data.ScriptTagProxy = function(config){
7280     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7281     Roo.apply(this, config);
7282     this.head = document.getElementsByTagName("head")[0];
7283 };
7284
7285 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7286
7287 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7288     /**
7289      * @cfg {String} url The URL from which to request the data object.
7290      */
7291     /**
7292      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7293      */
7294     timeout : 30000,
7295     /**
7296      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7297      * the server the name of the callback function set up by the load call to process the returned data object.
7298      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7299      * javascript output which calls this named function passing the data object as its only parameter.
7300      */
7301     callbackParam : "callback",
7302     /**
7303      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7304      * name to the request.
7305      */
7306     nocache : true,
7307
7308     /**
7309      * Load data from the configured URL, read the data object into
7310      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7311      * process that block using the passed callback.
7312      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7313      * for the request to the remote server.
7314      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7315      * object into a block of Roo.data.Records.
7316      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7317      * The function must be passed <ul>
7318      * <li>The Record block object</li>
7319      * <li>The "arg" argument from the load function</li>
7320      * <li>A boolean success indicator</li>
7321      * </ul>
7322      * @param {Object} scope The scope in which to call the callback
7323      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7324      */
7325     load : function(params, reader, callback, scope, arg){
7326         if(this.fireEvent("beforeload", this, params) !== false){
7327
7328             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7329
7330             var url = this.url;
7331             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7332             if(this.nocache){
7333                 url += "&_dc=" + (new Date().getTime());
7334             }
7335             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7336             var trans = {
7337                 id : transId,
7338                 cb : "stcCallback"+transId,
7339                 scriptId : "stcScript"+transId,
7340                 params : params,
7341                 arg : arg,
7342                 url : url,
7343                 callback : callback,
7344                 scope : scope,
7345                 reader : reader
7346             };
7347             var conn = this;
7348
7349             window[trans.cb] = function(o){
7350                 conn.handleResponse(o, trans);
7351             };
7352
7353             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7354
7355             if(this.autoAbort !== false){
7356                 this.abort();
7357             }
7358
7359             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7360
7361             var script = document.createElement("script");
7362             script.setAttribute("src", url);
7363             script.setAttribute("type", "text/javascript");
7364             script.setAttribute("id", trans.scriptId);
7365             this.head.appendChild(script);
7366
7367             this.trans = trans;
7368         }else{
7369             callback.call(scope||this, null, arg, false);
7370         }
7371     },
7372
7373     // private
7374     isLoading : function(){
7375         return this.trans ? true : false;
7376     },
7377
7378     /**
7379      * Abort the current server request.
7380      */
7381     abort : function(){
7382         if(this.isLoading()){
7383             this.destroyTrans(this.trans);
7384         }
7385     },
7386
7387     // private
7388     destroyTrans : function(trans, isLoaded){
7389         this.head.removeChild(document.getElementById(trans.scriptId));
7390         clearTimeout(trans.timeoutId);
7391         if(isLoaded){
7392             window[trans.cb] = undefined;
7393             try{
7394                 delete window[trans.cb];
7395             }catch(e){}
7396         }else{
7397             // if hasn't been loaded, wait for load to remove it to prevent script error
7398             window[trans.cb] = function(){
7399                 window[trans.cb] = undefined;
7400                 try{
7401                     delete window[trans.cb];
7402                 }catch(e){}
7403             };
7404         }
7405     },
7406
7407     // private
7408     handleResponse : function(o, trans){
7409         this.trans = false;
7410         this.destroyTrans(trans, true);
7411         var result;
7412         try {
7413             result = trans.reader.readRecords(o);
7414         }catch(e){
7415             this.fireEvent("loadexception", this, o, trans.arg, e);
7416             trans.callback.call(trans.scope||window, null, trans.arg, false);
7417             return;
7418         }
7419         this.fireEvent("load", this, o, trans.arg);
7420         trans.callback.call(trans.scope||window, result, trans.arg, true);
7421     },
7422
7423     // private
7424     handleFailure : function(trans){
7425         this.trans = false;
7426         this.destroyTrans(trans, false);
7427         this.fireEvent("loadexception", this, null, trans.arg);
7428         trans.callback.call(trans.scope||window, null, trans.arg, false);
7429     }
7430 });/*
7431  * Based on:
7432  * Ext JS Library 1.1.1
7433  * Copyright(c) 2006-2007, Ext JS, LLC.
7434  *
7435  * Originally Released Under LGPL - original licence link has changed is not relivant.
7436  *
7437  * Fork - LGPL
7438  * <script type="text/javascript">
7439  */
7440
7441 /**
7442  * @class Roo.data.JsonReader
7443  * @extends Roo.data.DataReader
7444  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7445  * based on mappings in a provided Roo.data.Record constructor.
7446  * 
7447  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7448  * in the reply previously. 
7449  * 
7450  * <p>
7451  * Example code:
7452  * <pre><code>
7453 var RecordDef = Roo.data.Record.create([
7454     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7455     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7456 ]);
7457 var myReader = new Roo.data.JsonReader({
7458     totalProperty: "results",    // The property which contains the total dataset size (optional)
7459     root: "rows",                // The property which contains an Array of row objects
7460     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7461 }, RecordDef);
7462 </code></pre>
7463  * <p>
7464  * This would consume a JSON file like this:
7465  * <pre><code>
7466 { 'results': 2, 'rows': [
7467     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7468     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7469 }
7470 </code></pre>
7471  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7472  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7473  * paged from the remote server.
7474  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7475  * @cfg {String} root name of the property which contains the Array of row objects.
7476  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7477  * @constructor
7478  * Create a new JsonReader
7479  * @param {Object} meta Metadata configuration options
7480  * @param {Object} recordType Either an Array of field definition objects,
7481  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7482  */
7483 Roo.data.JsonReader = function(meta, recordType){
7484     
7485     meta = meta || {};
7486     // set some defaults:
7487     Roo.applyIf(meta, {
7488         totalProperty: 'total',
7489         successProperty : 'success',
7490         root : 'data',
7491         id : 'id'
7492     });
7493     
7494     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7495 };
7496 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7497     
7498     /**
7499      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7500      * Used by Store query builder to append _requestMeta to params.
7501      * 
7502      */
7503     metaFromRemote : false,
7504     /**
7505      * This method is only used by a DataProxy which has retrieved data from a remote server.
7506      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7507      * @return {Object} data A data block which is used by an Roo.data.Store object as
7508      * a cache of Roo.data.Records.
7509      */
7510     read : function(response){
7511         var json = response.responseText;
7512        
7513         var o = /* eval:var:o */ eval("("+json+")");
7514         if(!o) {
7515             throw {message: "JsonReader.read: Json object not found"};
7516         }
7517         
7518         if(o.metaData){
7519             
7520             delete this.ef;
7521             this.metaFromRemote = true;
7522             this.meta = o.metaData;
7523             this.recordType = Roo.data.Record.create(o.metaData.fields);
7524             this.onMetaChange(this.meta, this.recordType, o);
7525         }
7526         return this.readRecords(o);
7527     },
7528
7529     // private function a store will implement
7530     onMetaChange : function(meta, recordType, o){
7531
7532     },
7533
7534     /**
7535          * @ignore
7536          */
7537     simpleAccess: function(obj, subsc) {
7538         return obj[subsc];
7539     },
7540
7541         /**
7542          * @ignore
7543          */
7544     getJsonAccessor: function(){
7545         var re = /[\[\.]/;
7546         return function(expr) {
7547             try {
7548                 return(re.test(expr))
7549                     ? new Function("obj", "return obj." + expr)
7550                     : function(obj){
7551                         return obj[expr];
7552                     };
7553             } catch(e){}
7554             return Roo.emptyFn;
7555         };
7556     }(),
7557
7558     /**
7559      * Create a data block containing Roo.data.Records from an XML document.
7560      * @param {Object} o An object which contains an Array of row objects in the property specified
7561      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7562      * which contains the total size of the dataset.
7563      * @return {Object} data A data block which is used by an Roo.data.Store object as
7564      * a cache of Roo.data.Records.
7565      */
7566     readRecords : function(o){
7567         /**
7568          * After any data loads, the raw JSON data is available for further custom processing.
7569          * @type Object
7570          */
7571         this.o = o;
7572         var s = this.meta, Record = this.recordType,
7573             f = Record.prototype.fields, fi = f.items, fl = f.length;
7574
7575 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7576         if (!this.ef) {
7577             if(s.totalProperty) {
7578                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7579                 }
7580                 if(s.successProperty) {
7581                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7582                 }
7583                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7584                 if (s.id) {
7585                         var g = this.getJsonAccessor(s.id);
7586                         this.getId = function(rec) {
7587                                 var r = g(rec);
7588                                 return (r === undefined || r === "") ? null : r;
7589                         };
7590                 } else {
7591                         this.getId = function(){return null;};
7592                 }
7593             this.ef = [];
7594             for(var jj = 0; jj < fl; jj++){
7595                 f = fi[jj];
7596                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7597                 this.ef[jj] = this.getJsonAccessor(map);
7598             }
7599         }
7600
7601         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7602         if(s.totalProperty){
7603             var vt = parseInt(this.getTotal(o), 10);
7604             if(!isNaN(vt)){
7605                 totalRecords = vt;
7606             }
7607         }
7608         if(s.successProperty){
7609             var vs = this.getSuccess(o);
7610             if(vs === false || vs === 'false'){
7611                 success = false;
7612             }
7613         }
7614         var records = [];
7615             for(var i = 0; i < c; i++){
7616                     var n = root[i];
7617                 var values = {};
7618                 var id = this.getId(n);
7619                 for(var j = 0; j < fl; j++){
7620                     f = fi[j];
7621                 var v = this.ef[j](n);
7622                 if (!f.convert) {
7623                     Roo.log('missing convert for ' + f.name);
7624                     Roo.log(f);
7625                     continue;
7626                 }
7627                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7628                 }
7629                 var record = new Record(values, id);
7630                 record.json = n;
7631                 records[i] = record;
7632             }
7633             return {
7634             raw : o,
7635                 success : success,
7636                 records : records,
7637                 totalRecords : totalRecords
7638             };
7639     }
7640 });/*
7641  * Based on:
7642  * Ext JS Library 1.1.1
7643  * Copyright(c) 2006-2007, Ext JS, LLC.
7644  *
7645  * Originally Released Under LGPL - original licence link has changed is not relivant.
7646  *
7647  * Fork - LGPL
7648  * <script type="text/javascript">
7649  */
7650
7651 /**
7652  * @class Roo.data.ArrayReader
7653  * @extends Roo.data.DataReader
7654  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7655  * Each element of that Array represents a row of data fields. The
7656  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7657  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7658  * <p>
7659  * Example code:.
7660  * <pre><code>
7661 var RecordDef = Roo.data.Record.create([
7662     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7663     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7664 ]);
7665 var myReader = new Roo.data.ArrayReader({
7666     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7667 }, RecordDef);
7668 </code></pre>
7669  * <p>
7670  * This would consume an Array like this:
7671  * <pre><code>
7672 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7673   </code></pre>
7674  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7675  * @constructor
7676  * Create a new JsonReader
7677  * @param {Object} meta Metadata configuration options.
7678  * @param {Object} recordType Either an Array of field definition objects
7679  * as specified to {@link Roo.data.Record#create},
7680  * or an {@link Roo.data.Record} object
7681  * created using {@link Roo.data.Record#create}.
7682  */
7683 Roo.data.ArrayReader = function(meta, recordType){
7684     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7685 };
7686
7687 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7688     /**
7689      * Create a data block containing Roo.data.Records from an XML document.
7690      * @param {Object} o An Array of row objects which represents the dataset.
7691      * @return {Object} data A data block which is used by an Roo.data.Store object as
7692      * a cache of Roo.data.Records.
7693      */
7694     readRecords : function(o){
7695         var sid = this.meta ? this.meta.id : null;
7696         var recordType = this.recordType, fields = recordType.prototype.fields;
7697         var records = [];
7698         var root = o;
7699             for(var i = 0; i < root.length; i++){
7700                     var n = root[i];
7701                 var values = {};
7702                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7703                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7704                 var f = fields.items[j];
7705                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7706                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7707                 v = f.convert(v);
7708                 values[f.name] = v;
7709             }
7710                 var record = new recordType(values, id);
7711                 record.json = n;
7712                 records[records.length] = record;
7713             }
7714             return {
7715                 records : records,
7716                 totalRecords : records.length
7717             };
7718     }
7719 });/*
7720  * - LGPL
7721  * * 
7722  */
7723
7724 /**
7725  * @class Roo.bootstrap.ComboBox
7726  * @extends Roo.bootstrap.TriggerField
7727  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7728  * @cfg {Boolean} append (true|false) default false
7729  * @constructor
7730  * Create a new ComboBox.
7731  * @param {Object} config Configuration options
7732  */
7733 Roo.bootstrap.ComboBox = function(config){
7734     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7735     this.addEvents({
7736         /**
7737          * @event expand
7738          * Fires when the dropdown list is expanded
7739              * @param {Roo.bootstrap.ComboBox} combo This combo box
7740              */
7741         'expand' : true,
7742         /**
7743          * @event collapse
7744          * Fires when the dropdown list is collapsed
7745              * @param {Roo.bootstrap.ComboBox} combo This combo box
7746              */
7747         'collapse' : true,
7748         /**
7749          * @event beforeselect
7750          * Fires before a list item is selected. Return false to cancel the selection.
7751              * @param {Roo.bootstrap.ComboBox} combo This combo box
7752              * @param {Roo.data.Record} record The data record returned from the underlying store
7753              * @param {Number} index The index of the selected item in the dropdown list
7754              */
7755         'beforeselect' : true,
7756         /**
7757          * @event select
7758          * Fires when a list item is selected
7759              * @param {Roo.bootstrap.ComboBox} combo This combo box
7760              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7761              * @param {Number} index The index of the selected item in the dropdown list
7762              */
7763         'select' : true,
7764         /**
7765          * @event beforequery
7766          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7767          * The event object passed has these properties:
7768              * @param {Roo.bootstrap.ComboBox} combo This combo box
7769              * @param {String} query The query
7770              * @param {Boolean} forceAll true to force "all" query
7771              * @param {Boolean} cancel true to cancel the query
7772              * @param {Object} e The query event object
7773              */
7774         'beforequery': true,
7775          /**
7776          * @event add
7777          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7778              * @param {Roo.bootstrap.ComboBox} combo This combo box
7779              */
7780         'add' : true,
7781         /**
7782          * @event edit
7783          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7784              * @param {Roo.bootstrap.ComboBox} combo This combo box
7785              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7786              */
7787         'edit' : true,
7788         /**
7789          * @event remove
7790          * Fires when the remove value from the combobox array
7791              * @param {Roo.bootstrap.ComboBox} combo This combo box
7792              */
7793         'remove' : true
7794         
7795     });
7796     
7797     
7798     this.selectedIndex = -1;
7799     if(this.mode == 'local'){
7800         if(config.queryDelay === undefined){
7801             this.queryDelay = 10;
7802         }
7803         if(config.minChars === undefined){
7804             this.minChars = 0;
7805         }
7806     }
7807 };
7808
7809 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7810      
7811     /**
7812      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7813      * rendering into an Roo.Editor, defaults to false)
7814      */
7815     /**
7816      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7817      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7818      */
7819     /**
7820      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7821      */
7822     /**
7823      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7824      * the dropdown list (defaults to undefined, with no header element)
7825      */
7826
7827      /**
7828      * @cfg {String/Roo.Template} tpl The template to use to render the output
7829      */
7830      
7831      /**
7832      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7833      */
7834     listWidth: undefined,
7835     /**
7836      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7837      * mode = 'remote' or 'text' if mode = 'local')
7838      */
7839     displayField: undefined,
7840     /**
7841      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7842      * mode = 'remote' or 'value' if mode = 'local'). 
7843      * Note: use of a valueField requires the user make a selection
7844      * in order for a value to be mapped.
7845      */
7846     valueField: undefined,
7847     
7848     
7849     /**
7850      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7851      * field's data value (defaults to the underlying DOM element's name)
7852      */
7853     hiddenName: undefined,
7854     /**
7855      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7856      */
7857     listClass: '',
7858     /**
7859      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7860      */
7861     selectedClass: 'active',
7862     
7863     /**
7864      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7865      */
7866     shadow:'sides',
7867     /**
7868      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7869      * anchor positions (defaults to 'tl-bl')
7870      */
7871     listAlign: 'tl-bl?',
7872     /**
7873      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7874      */
7875     maxHeight: 300,
7876     /**
7877      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7878      * query specified by the allQuery config option (defaults to 'query')
7879      */
7880     triggerAction: 'query',
7881     /**
7882      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7883      * (defaults to 4, does not apply if editable = false)
7884      */
7885     minChars : 4,
7886     /**
7887      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7888      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7889      */
7890     typeAhead: false,
7891     /**
7892      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7893      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7894      */
7895     queryDelay: 500,
7896     /**
7897      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7898      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7899      */
7900     pageSize: 0,
7901     /**
7902      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7903      * when editable = true (defaults to false)
7904      */
7905     selectOnFocus:false,
7906     /**
7907      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7908      */
7909     queryParam: 'query',
7910     /**
7911      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7912      * when mode = 'remote' (defaults to 'Loading...')
7913      */
7914     loadingText: 'Loading...',
7915     /**
7916      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7917      */
7918     resizable: false,
7919     /**
7920      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7921      */
7922     handleHeight : 8,
7923     /**
7924      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7925      * traditional select (defaults to true)
7926      */
7927     editable: true,
7928     /**
7929      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7930      */
7931     allQuery: '',
7932     /**
7933      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7934      */
7935     mode: 'remote',
7936     /**
7937      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7938      * listWidth has a higher value)
7939      */
7940     minListWidth : 70,
7941     /**
7942      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7943      * allow the user to set arbitrary text into the field (defaults to false)
7944      */
7945     forceSelection:false,
7946     /**
7947      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7948      * if typeAhead = true (defaults to 250)
7949      */
7950     typeAheadDelay : 250,
7951     /**
7952      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7953      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7954      */
7955     valueNotFoundText : undefined,
7956     /**
7957      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7958      */
7959     blockFocus : false,
7960     
7961     /**
7962      * @cfg {Boolean} disableClear Disable showing of clear button.
7963      */
7964     disableClear : false,
7965     /**
7966      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7967      */
7968     alwaysQuery : false,
7969     
7970     /**
7971      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7972      */
7973     multiple : false,
7974     
7975     //private
7976     addicon : false,
7977     editicon: false,
7978     
7979     page: 0,
7980     hasQuery: false,
7981     append: false,
7982     loadNext: false,
7983     item: [],
7984     
7985     // element that contains real text value.. (when hidden is used..)
7986      
7987     // private
7988     initEvents: function(){
7989         
7990         if (!this.store) {
7991             throw "can not find store for combo";
7992         }
7993         this.store = Roo.factory(this.store, Roo.data);
7994         
7995         
7996         
7997         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
7998         
7999         
8000         if(this.hiddenName){
8001             
8002             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8003             
8004             this.hiddenField.dom.value =
8005                 this.hiddenValue !== undefined ? this.hiddenValue :
8006                 this.value !== undefined ? this.value : '';
8007
8008             // prevent input submission
8009             this.el.dom.removeAttribute('name');
8010             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8011              
8012              
8013         }
8014         //if(Roo.isGecko){
8015         //    this.el.dom.setAttribute('autocomplete', 'off');
8016         //}
8017
8018         var cls = 'x-combo-list';
8019         this.list = this.el.select('ul.dropdown-menu',true).first();
8020
8021         //this.list = new Roo.Layer({
8022         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8023         //});
8024         
8025         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8026         this.list.setWidth(lw);
8027         
8028         this.list.on('mouseover', this.onViewOver, this);
8029         this.list.on('mousemove', this.onViewMove, this);
8030         
8031         this.list.on('scroll', this.onViewScroll, this);
8032         
8033         /*
8034         this.list.swallowEvent('mousewheel');
8035         this.assetHeight = 0;
8036
8037         if(this.title){
8038             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8039             this.assetHeight += this.header.getHeight();
8040         }
8041
8042         this.innerList = this.list.createChild({cls:cls+'-inner'});
8043         this.innerList.on('mouseover', this.onViewOver, this);
8044         this.innerList.on('mousemove', this.onViewMove, this);
8045         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8046         
8047         if(this.allowBlank && !this.pageSize && !this.disableClear){
8048             this.footer = this.list.createChild({cls:cls+'-ft'});
8049             this.pageTb = new Roo.Toolbar(this.footer);
8050            
8051         }
8052         if(this.pageSize){
8053             this.footer = this.list.createChild({cls:cls+'-ft'});
8054             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8055                     {pageSize: this.pageSize});
8056             
8057         }
8058         
8059         if (this.pageTb && this.allowBlank && !this.disableClear) {
8060             var _this = this;
8061             this.pageTb.add(new Roo.Toolbar.Fill(), {
8062                 cls: 'x-btn-icon x-btn-clear',
8063                 text: '&#160;',
8064                 handler: function()
8065                 {
8066                     _this.collapse();
8067                     _this.clearValue();
8068                     _this.onSelect(false, -1);
8069                 }
8070             });
8071         }
8072         if (this.footer) {
8073             this.assetHeight += this.footer.getHeight();
8074         }
8075         */
8076             
8077         if(!this.tpl){
8078             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8079         }
8080
8081         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8082             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8083         });
8084         //this.view.wrapEl.setDisplayed(false);
8085         this.view.on('click', this.onViewClick, this);
8086         
8087         
8088         
8089         this.store.on('beforeload', this.onBeforeLoad, this);
8090         this.store.on('load', this.onLoad, this);
8091         this.store.on('loadexception', this.onLoadException, this);
8092         /*
8093         if(this.resizable){
8094             this.resizer = new Roo.Resizable(this.list,  {
8095                pinned:true, handles:'se'
8096             });
8097             this.resizer.on('resize', function(r, w, h){
8098                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8099                 this.listWidth = w;
8100                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8101                 this.restrictHeight();
8102             }, this);
8103             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8104         }
8105         */
8106         if(!this.editable){
8107             this.editable = true;
8108             this.setEditable(false);
8109         }
8110         
8111         /*
8112         
8113         if (typeof(this.events.add.listeners) != 'undefined') {
8114             
8115             this.addicon = this.wrap.createChild(
8116                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8117        
8118             this.addicon.on('click', function(e) {
8119                 this.fireEvent('add', this);
8120             }, this);
8121         }
8122         if (typeof(this.events.edit.listeners) != 'undefined') {
8123             
8124             this.editicon = this.wrap.createChild(
8125                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8126             if (this.addicon) {
8127                 this.editicon.setStyle('margin-left', '40px');
8128             }
8129             this.editicon.on('click', function(e) {
8130                 
8131                 // we fire even  if inothing is selected..
8132                 this.fireEvent('edit', this, this.lastData );
8133                 
8134             }, this);
8135         }
8136         */
8137         
8138         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8139             "up" : function(e){
8140                 this.inKeyMode = true;
8141                 this.selectPrev();
8142             },
8143
8144             "down" : function(e){
8145                 if(!this.isExpanded()){
8146                     this.onTriggerClick();
8147                 }else{
8148                     this.inKeyMode = true;
8149                     this.selectNext();
8150                 }
8151             },
8152
8153             "enter" : function(e){
8154                 this.onViewClick();
8155                 //return true;
8156             },
8157
8158             "esc" : function(e){
8159                 this.collapse();
8160             },
8161
8162             "tab" : function(e){
8163                 this.collapse();
8164                 
8165                 if(this.fireEvent("specialkey", this, e)){
8166                     this.onViewClick(false);
8167                 }
8168                 
8169                 return true;
8170             },
8171
8172             scope : this,
8173
8174             doRelay : function(foo, bar, hname){
8175                 if(hname == 'down' || this.scope.isExpanded()){
8176                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8177                 }
8178                 return true;
8179             },
8180
8181             forceKeyDown: true
8182         });
8183         
8184         
8185         this.queryDelay = Math.max(this.queryDelay || 10,
8186                 this.mode == 'local' ? 10 : 250);
8187         
8188         
8189         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8190         
8191         if(this.typeAhead){
8192             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8193         }
8194         if(this.editable !== false){
8195             this.inputEl().on("keyup", this.onKeyUp, this);
8196         }
8197         if(this.forceSelection){
8198             this.on('blur', this.doForce, this);
8199         }
8200         
8201         if(this.multiple){
8202             this.choices = this.el.select('ul.select2-choices', true).first();
8203             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8204         }
8205     },
8206
8207     onDestroy : function(){
8208         if(this.view){
8209             this.view.setStore(null);
8210             this.view.el.removeAllListeners();
8211             this.view.el.remove();
8212             this.view.purgeListeners();
8213         }
8214         if(this.list){
8215             this.list.dom.innerHTML  = '';
8216         }
8217         if(this.store){
8218             this.store.un('beforeload', this.onBeforeLoad, this);
8219             this.store.un('load', this.onLoad, this);
8220             this.store.un('loadexception', this.onLoadException, this);
8221         }
8222         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8223     },
8224
8225     // private
8226     fireKey : function(e){
8227         if(e.isNavKeyPress() && !this.list.isVisible()){
8228             this.fireEvent("specialkey", this, e);
8229         }
8230     },
8231
8232     // private
8233     onResize: function(w, h){
8234 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8235 //        
8236 //        if(typeof w != 'number'){
8237 //            // we do not handle it!?!?
8238 //            return;
8239 //        }
8240 //        var tw = this.trigger.getWidth();
8241 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8242 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8243 //        var x = w - tw;
8244 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8245 //            
8246 //        //this.trigger.setStyle('left', x+'px');
8247 //        
8248 //        if(this.list && this.listWidth === undefined){
8249 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8250 //            this.list.setWidth(lw);
8251 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8252 //        }
8253         
8254     
8255         
8256     },
8257
8258     /**
8259      * Allow or prevent the user from directly editing the field text.  If false is passed,
8260      * the user will only be able to select from the items defined in the dropdown list.  This method
8261      * is the runtime equivalent of setting the 'editable' config option at config time.
8262      * @param {Boolean} value True to allow the user to directly edit the field text
8263      */
8264     setEditable : function(value){
8265         if(value == this.editable){
8266             return;
8267         }
8268         this.editable = value;
8269         if(!value){
8270             this.inputEl().dom.setAttribute('readOnly', true);
8271             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8272             this.inputEl().addClass('x-combo-noedit');
8273         }else{
8274             this.inputEl().dom.setAttribute('readOnly', false);
8275             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8276             this.inputEl().removeClass('x-combo-noedit');
8277         }
8278     },
8279
8280     // private
8281     
8282     onBeforeLoad : function(combo,opts){
8283         if(!this.hasFocus){
8284             return;
8285         }
8286          if (!opts.add) {
8287             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8288          }
8289         this.restrictHeight();
8290         this.selectedIndex = -1;
8291     },
8292
8293     // private
8294     onLoad : function(){
8295         
8296         this.hasQuery = false;
8297         
8298         if(!this.hasFocus){
8299             return;
8300         }
8301         
8302         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8303             this.loading.hide();
8304         }
8305         
8306         if(this.store.getCount() > 0){
8307             this.expand();
8308             this.restrictHeight();
8309             if(this.lastQuery == this.allQuery){
8310                 if(this.editable){
8311                     this.inputEl().dom.select();
8312                 }
8313                 if(!this.selectByValue(this.value, true)){
8314                     this.select(0, true);
8315                 }
8316             }else{
8317                 this.selectNext();
8318                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8319                     this.taTask.delay(this.typeAheadDelay);
8320                 }
8321             }
8322         }else{
8323             this.onEmptyResults();
8324         }
8325         
8326         //this.el.focus();
8327     },
8328     // private
8329     onLoadException : function()
8330     {
8331         this.hasQuery = false;
8332         
8333         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8334             this.loading.hide();
8335         }
8336         
8337         this.collapse();
8338         Roo.log(this.store.reader.jsonData);
8339         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8340             // fixme
8341             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8342         }
8343         
8344         
8345     },
8346     // private
8347     onTypeAhead : function(){
8348         if(this.store.getCount() > 0){
8349             var r = this.store.getAt(0);
8350             var newValue = r.data[this.displayField];
8351             var len = newValue.length;
8352             var selStart = this.getRawValue().length;
8353             
8354             if(selStart != len){
8355                 this.setRawValue(newValue);
8356                 this.selectText(selStart, newValue.length);
8357             }
8358         }
8359     },
8360
8361     // private
8362     onSelect : function(record, index){
8363         
8364         if(this.fireEvent('beforeselect', this, record, index) !== false){
8365         
8366             this.setFromData(index > -1 ? record.data : false);
8367             
8368             this.collapse();
8369             this.fireEvent('select', this, record, index);
8370         }
8371     },
8372
8373     /**
8374      * Returns the currently selected field value or empty string if no value is set.
8375      * @return {String} value The selected value
8376      */
8377     getValue : function(){
8378         
8379         if(this.multiple){
8380             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8381         }
8382         
8383         if(this.valueField){
8384             return typeof this.value != 'undefined' ? this.value : '';
8385         }else{
8386             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8387         }
8388     },
8389
8390     /**
8391      * Clears any text/value currently set in the field
8392      */
8393     clearValue : function(){
8394         if(this.hiddenField){
8395             this.hiddenField.dom.value = '';
8396         }
8397         this.value = '';
8398         this.setRawValue('');
8399         this.lastSelectionText = '';
8400         
8401     },
8402
8403     /**
8404      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8405      * will be displayed in the field.  If the value does not match the data value of an existing item,
8406      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8407      * Otherwise the field will be blank (although the value will still be set).
8408      * @param {String} value The value to match
8409      */
8410     setValue : function(v){
8411         if(this.multiple){
8412             this.syncValue();
8413             return;
8414         }
8415         
8416         var text = v;
8417         if(this.valueField){
8418             var r = this.findRecord(this.valueField, v);
8419             if(r){
8420                 text = r.data[this.displayField];
8421             }else if(this.valueNotFoundText !== undefined){
8422                 text = this.valueNotFoundText;
8423             }
8424         }
8425         this.lastSelectionText = text;
8426         if(this.hiddenField){
8427             this.hiddenField.dom.value = v;
8428         }
8429         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8430         this.value = v;
8431     },
8432     /**
8433      * @property {Object} the last set data for the element
8434      */
8435     
8436     lastData : false,
8437     /**
8438      * Sets the value of the field based on a object which is related to the record format for the store.
8439      * @param {Object} value the value to set as. or false on reset?
8440      */
8441     setFromData : function(o){
8442         
8443         if(this.multiple){
8444             this.addItem(o);
8445             return;
8446         }
8447             
8448         var dv = ''; // display value
8449         var vv = ''; // value value..
8450         this.lastData = o;
8451         if (this.displayField) {
8452             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8453         } else {
8454             // this is an error condition!!!
8455             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8456         }
8457         
8458         if(this.valueField){
8459             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8460         }
8461         
8462         if(this.hiddenField){
8463             this.hiddenField.dom.value = vv;
8464             
8465             this.lastSelectionText = dv;
8466             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8467             this.value = vv;
8468             return;
8469         }
8470         // no hidden field.. - we store the value in 'value', but still display
8471         // display field!!!!
8472         this.lastSelectionText = dv;
8473         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8474         this.value = vv;
8475         
8476         
8477     },
8478     // private
8479     reset : function(){
8480         // overridden so that last data is reset..
8481         this.setValue(this.originalValue);
8482         this.clearInvalid();
8483         this.lastData = false;
8484         if (this.view) {
8485             this.view.clearSelections();
8486         }
8487     },
8488     // private
8489     findRecord : function(prop, value){
8490         var record;
8491         if(this.store.getCount() > 0){
8492             this.store.each(function(r){
8493                 if(r.data[prop] == value){
8494                     record = r;
8495                     return false;
8496                 }
8497                 return true;
8498             });
8499         }
8500         return record;
8501     },
8502     
8503     getName: function()
8504     {
8505         // returns hidden if it's set..
8506         if (!this.rendered) {return ''};
8507         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8508         
8509     },
8510     // private
8511     onViewMove : function(e, t){
8512         this.inKeyMode = false;
8513     },
8514
8515     // private
8516     onViewOver : function(e, t){
8517         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8518             return;
8519         }
8520         var item = this.view.findItemFromChild(t);
8521         if(item){
8522             var index = this.view.indexOf(item);
8523             this.select(index, false);
8524         }
8525     },
8526
8527     // private
8528     onViewClick : function(doFocus)
8529     {
8530         var index = this.view.getSelectedIndexes()[0];
8531         var r = this.store.getAt(index);
8532         if(r){
8533             this.onSelect(r, index);
8534         }
8535         if(doFocus !== false && !this.blockFocus){
8536             this.inputEl().focus();
8537         }
8538     },
8539
8540     // private
8541     restrictHeight : function(){
8542         //this.innerList.dom.style.height = '';
8543         //var inner = this.innerList.dom;
8544         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8545         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8546         //this.list.beginUpdate();
8547         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8548         this.list.alignTo(this.inputEl(), this.listAlign);
8549         //this.list.endUpdate();
8550     },
8551
8552     // private
8553     onEmptyResults : function(){
8554         this.collapse();
8555     },
8556
8557     /**
8558      * Returns true if the dropdown list is expanded, else false.
8559      */
8560     isExpanded : function(){
8561         return this.list.isVisible();
8562     },
8563
8564     /**
8565      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8566      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8567      * @param {String} value The data value of the item to select
8568      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8569      * selected item if it is not currently in view (defaults to true)
8570      * @return {Boolean} True if the value matched an item in the list, else false
8571      */
8572     selectByValue : function(v, scrollIntoView){
8573         if(v !== undefined && v !== null){
8574             var r = this.findRecord(this.valueField || this.displayField, v);
8575             if(r){
8576                 this.select(this.store.indexOf(r), scrollIntoView);
8577                 return true;
8578             }
8579         }
8580         return false;
8581     },
8582
8583     /**
8584      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8585      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8586      * @param {Number} index The zero-based index of the list item to select
8587      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8588      * selected item if it is not currently in view (defaults to true)
8589      */
8590     select : function(index, scrollIntoView){
8591         this.selectedIndex = index;
8592         this.view.select(index);
8593         if(scrollIntoView !== false){
8594             var el = this.view.getNode(index);
8595             if(el){
8596                 //this.innerList.scrollChildIntoView(el, false);
8597                 
8598             }
8599         }
8600     },
8601
8602     // private
8603     selectNext : function(){
8604         var ct = this.store.getCount();
8605         if(ct > 0){
8606             if(this.selectedIndex == -1){
8607                 this.select(0);
8608             }else if(this.selectedIndex < ct-1){
8609                 this.select(this.selectedIndex+1);
8610             }
8611         }
8612     },
8613
8614     // private
8615     selectPrev : function(){
8616         var ct = this.store.getCount();
8617         if(ct > 0){
8618             if(this.selectedIndex == -1){
8619                 this.select(0);
8620             }else if(this.selectedIndex != 0){
8621                 this.select(this.selectedIndex-1);
8622             }
8623         }
8624     },
8625
8626     // private
8627     onKeyUp : function(e){
8628         if(this.editable !== false && !e.isSpecialKey()){
8629             this.lastKey = e.getKey();
8630             this.dqTask.delay(this.queryDelay);
8631         }
8632     },
8633
8634     // private
8635     validateBlur : function(){
8636         return !this.list || !this.list.isVisible();   
8637     },
8638
8639     // private
8640     initQuery : function(){
8641         this.doQuery(this.getRawValue());
8642     },
8643
8644     // private
8645     doForce : function(){
8646         if(this.el.dom.value.length > 0){
8647             this.el.dom.value =
8648                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8649              
8650         }
8651     },
8652
8653     /**
8654      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8655      * query allowing the query action to be canceled if needed.
8656      * @param {String} query The SQL query to execute
8657      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8658      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8659      * saved in the current store (defaults to false)
8660      */
8661     doQuery : function(q, forceAll){
8662         
8663         if(q === undefined || q === null){
8664             q = '';
8665         }
8666         var qe = {
8667             query: q,
8668             forceAll: forceAll,
8669             combo: this,
8670             cancel:false
8671         };
8672         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8673             return false;
8674         }
8675         q = qe.query;
8676         
8677         forceAll = qe.forceAll;
8678         if(forceAll === true || (q.length >= this.minChars)){
8679             
8680             this.hasQuery = true;
8681             
8682             if(this.lastQuery != q || this.alwaysQuery){
8683                 this.lastQuery = q;
8684                 if(this.mode == 'local'){
8685                     this.selectedIndex = -1;
8686                     if(forceAll){
8687                         this.store.clearFilter();
8688                     }else{
8689                         this.store.filter(this.displayField, q);
8690                     }
8691                     this.onLoad();
8692                 }else{
8693                     this.store.baseParams[this.queryParam] = q;
8694                     
8695                     var options = {params : this.getParams(q)};
8696                     
8697                     if(this.loadNext){
8698                         options.add = true;
8699                         options.params.start = this.page * this.pageSize;
8700                     }
8701                     
8702                     this.store.load(options);
8703                     this.expand();
8704                 }
8705             }else{
8706                 this.selectedIndex = -1;
8707                 this.onLoad();   
8708             }
8709         }
8710         
8711         this.loadNext = false;
8712     },
8713
8714     // private
8715     getParams : function(q){
8716         var p = {};
8717         //p[this.queryParam] = q;
8718         
8719         if(this.pageSize){
8720             p.start = 0;
8721             p.limit = this.pageSize;
8722         }
8723         return p;
8724     },
8725
8726     /**
8727      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8728      */
8729     collapse : function(){
8730         if(!this.isExpanded()){
8731             return;
8732         }
8733         
8734         this.list.hide();
8735         Roo.get(document).un('mousedown', this.collapseIf, this);
8736         Roo.get(document).un('mousewheel', this.collapseIf, this);
8737         if (!this.editable) {
8738             Roo.get(document).un('keydown', this.listKeyPress, this);
8739         }
8740         this.fireEvent('collapse', this);
8741     },
8742
8743     // private
8744     collapseIf : function(e){
8745         var in_combo  = e.within(this.el);
8746         var in_list =  e.within(this.list);
8747         
8748         if (in_combo || in_list) {
8749             //e.stopPropagation();
8750             return;
8751         }
8752
8753         this.collapse();
8754         
8755     },
8756
8757     /**
8758      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8759      */
8760     expand : function(){
8761        
8762         if(this.isExpanded() || !this.hasFocus){
8763             return;
8764         }
8765          Roo.log('expand');
8766         this.list.alignTo(this.inputEl(), this.listAlign);
8767         this.list.show();
8768         Roo.get(document).on('mousedown', this.collapseIf, this);
8769         Roo.get(document).on('mousewheel', this.collapseIf, this);
8770         if (!this.editable) {
8771             Roo.get(document).on('keydown', this.listKeyPress, this);
8772         }
8773         
8774         this.fireEvent('expand', this);
8775     },
8776
8777     // private
8778     // Implements the default empty TriggerField.onTriggerClick function
8779     onTriggerClick : function()
8780     {
8781         Roo.log('trigger click');
8782         
8783         if(this.disabled){
8784             return;
8785         }
8786         
8787         this.page = 0;
8788         this.loadNext = false;
8789         
8790         if(this.isExpanded()){
8791             this.collapse();
8792             if (!this.blockFocus) {
8793                 this.inputEl().focus();
8794             }
8795             
8796         }else {
8797             this.hasFocus = true;
8798             if(this.triggerAction == 'all') {
8799                 this.doQuery(this.allQuery, true);
8800             } else {
8801                 this.doQuery(this.getRawValue());
8802             }
8803             if (!this.blockFocus) {
8804                 this.inputEl().focus();
8805             }
8806         }
8807     },
8808     listKeyPress : function(e)
8809     {
8810         //Roo.log('listkeypress');
8811         // scroll to first matching element based on key pres..
8812         if (e.isSpecialKey()) {
8813             return false;
8814         }
8815         var k = String.fromCharCode(e.getKey()).toUpperCase();
8816         //Roo.log(k);
8817         var match  = false;
8818         var csel = this.view.getSelectedNodes();
8819         var cselitem = false;
8820         if (csel.length) {
8821             var ix = this.view.indexOf(csel[0]);
8822             cselitem  = this.store.getAt(ix);
8823             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8824                 cselitem = false;
8825             }
8826             
8827         }
8828         
8829         this.store.each(function(v) { 
8830             if (cselitem) {
8831                 // start at existing selection.
8832                 if (cselitem.id == v.id) {
8833                     cselitem = false;
8834                 }
8835                 return true;
8836             }
8837                 
8838             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8839                 match = this.store.indexOf(v);
8840                 return false;
8841             }
8842             return true;
8843         }, this);
8844         
8845         if (match === false) {
8846             return true; // no more action?
8847         }
8848         // scroll to?
8849         this.view.select(match);
8850         var sn = Roo.get(this.view.getSelectedNodes()[0])
8851         //sn.scrollIntoView(sn.dom.parentNode, false);
8852     },
8853     
8854     onViewScroll : function(e, t){
8855         
8856         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8857             return;
8858         }
8859         
8860         this.hasQuery = true;
8861         
8862         this.loading = this.list.select('.loading', true).first();
8863         
8864         if(this.loading === null){
8865             this.list.createChild({
8866                 tag: 'div',
8867                 cls: 'loading select2-more-results select2-active',
8868                 html: 'Loading more results...'
8869             })
8870             
8871             this.loading = this.list.select('.loading', true).first();
8872             
8873             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8874             
8875             this.loading.hide();
8876         }
8877         
8878         this.loading.show();
8879         
8880         var _combo = this;
8881         
8882         this.page++;
8883         this.loadNext = true;
8884         
8885         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8886         
8887         return;
8888     },
8889     
8890     addItem : function(o)
8891     {   
8892         var dv = ''; // display value
8893         
8894         if (this.displayField) {
8895             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8896         } else {
8897             // this is an error condition!!!
8898             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8899         }
8900         
8901         if(!dv.length){
8902             return;
8903         }
8904         
8905         var choice = this.choices.createChild({
8906             tag: 'li',
8907             cls: 'select2-search-choice',
8908             cn: [
8909                 {
8910                     tag: 'div',
8911                     html: dv
8912                 },
8913                 {
8914                     tag: 'a',
8915                     href: '#',
8916                     cls: 'select2-search-choice-close',
8917                     tabindex: '-1'
8918                 }
8919             ]
8920             
8921         }, this.searchField);
8922         
8923         var close = choice.select('a.select2-search-choice-close', true).first()
8924         
8925         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8926         
8927         this.item.push(o);
8928         this.lastData = o;
8929         
8930         this.syncValue();
8931         
8932         this.inputEl().dom.value = '';
8933         
8934     },
8935     
8936     onRemoveItem : function(e, _self, o)
8937     {
8938         Roo.log('remove item');
8939         var index = this.item.indexOf(o.data) * 1;
8940         
8941         if( index < 0){
8942             Roo.log('not this item?!');
8943             return;
8944         }
8945         
8946         this.item.splice(index, 1);
8947         o.item.remove();
8948         
8949         this.syncValue();
8950         
8951         this.fireEvent('remove', this);
8952         
8953     },
8954     
8955     syncValue : function()
8956     {
8957         if(!this.item.length){
8958             this.clearValue();
8959             return;
8960         }
8961             
8962         var value = [];
8963         var _this = this;
8964         Roo.each(this.item, function(i){
8965             if(_this.valueField){
8966                 value.push(i[_this.valueField]);
8967                 return;
8968             }
8969
8970             value.push(i);
8971         });
8972
8973         this.value = value.join(',');
8974
8975         if(this.hiddenField){
8976             this.hiddenField.dom.value = this.value;
8977         }
8978     },
8979     
8980     clearItem : function()
8981     {
8982         if(!this.multiple){
8983             return;
8984         }
8985         
8986         this.item = [];
8987         
8988         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
8989            c.remove();
8990         });
8991         
8992         this.syncValue();
8993     }
8994     
8995     
8996
8997     /** 
8998     * @cfg {Boolean} grow 
8999     * @hide 
9000     */
9001     /** 
9002     * @cfg {Number} growMin 
9003     * @hide 
9004     */
9005     /** 
9006     * @cfg {Number} growMax 
9007     * @hide 
9008     */
9009     /**
9010      * @hide
9011      * @method autoSize
9012      */
9013 });
9014 /*
9015  * Based on:
9016  * Ext JS Library 1.1.1
9017  * Copyright(c) 2006-2007, Ext JS, LLC.
9018  *
9019  * Originally Released Under LGPL - original licence link has changed is not relivant.
9020  *
9021  * Fork - LGPL
9022  * <script type="text/javascript">
9023  */
9024
9025 /**
9026  * @class Roo.View
9027  * @extends Roo.util.Observable
9028  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9029  * This class also supports single and multi selection modes. <br>
9030  * Create a data model bound view:
9031  <pre><code>
9032  var store = new Roo.data.Store(...);
9033
9034  var view = new Roo.View({
9035     el : "my-element",
9036     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9037  
9038     singleSelect: true,
9039     selectedClass: "ydataview-selected",
9040     store: store
9041  });
9042
9043  // listen for node click?
9044  view.on("click", function(vw, index, node, e){
9045  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9046  });
9047
9048  // load XML data
9049  dataModel.load("foobar.xml");
9050  </code></pre>
9051  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9052  * <br><br>
9053  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9054  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9055  * 
9056  * Note: old style constructor is still suported (container, template, config)
9057  * 
9058  * @constructor
9059  * Create a new View
9060  * @param {Object} config The config object
9061  * 
9062  */
9063 Roo.View = function(config, depreciated_tpl, depreciated_config){
9064     
9065     if (typeof(depreciated_tpl) == 'undefined') {
9066         // new way.. - universal constructor.
9067         Roo.apply(this, config);
9068         this.el  = Roo.get(this.el);
9069     } else {
9070         // old format..
9071         this.el  = Roo.get(config);
9072         this.tpl = depreciated_tpl;
9073         Roo.apply(this, depreciated_config);
9074     }
9075     this.wrapEl  = this.el.wrap().wrap();
9076     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9077     
9078     
9079     if(typeof(this.tpl) == "string"){
9080         this.tpl = new Roo.Template(this.tpl);
9081     } else {
9082         // support xtype ctors..
9083         this.tpl = new Roo.factory(this.tpl, Roo);
9084     }
9085     
9086     
9087     this.tpl.compile();
9088    
9089   
9090     
9091      
9092     /** @private */
9093     this.addEvents({
9094         /**
9095          * @event beforeclick
9096          * Fires before a click is processed. Returns false to cancel the default action.
9097          * @param {Roo.View} this
9098          * @param {Number} index The index of the target node
9099          * @param {HTMLElement} node The target node
9100          * @param {Roo.EventObject} e The raw event object
9101          */
9102             "beforeclick" : true,
9103         /**
9104          * @event click
9105          * Fires when a template node is clicked.
9106          * @param {Roo.View} this
9107          * @param {Number} index The index of the target node
9108          * @param {HTMLElement} node The target node
9109          * @param {Roo.EventObject} e The raw event object
9110          */
9111             "click" : true,
9112         /**
9113          * @event dblclick
9114          * Fires when a template node is double clicked.
9115          * @param {Roo.View} this
9116          * @param {Number} index The index of the target node
9117          * @param {HTMLElement} node The target node
9118          * @param {Roo.EventObject} e The raw event object
9119          */
9120             "dblclick" : true,
9121         /**
9122          * @event contextmenu
9123          * Fires when a template node is right clicked.
9124          * @param {Roo.View} this
9125          * @param {Number} index The index of the target node
9126          * @param {HTMLElement} node The target node
9127          * @param {Roo.EventObject} e The raw event object
9128          */
9129             "contextmenu" : true,
9130         /**
9131          * @event selectionchange
9132          * Fires when the selected nodes change.
9133          * @param {Roo.View} this
9134          * @param {Array} selections Array of the selected nodes
9135          */
9136             "selectionchange" : true,
9137     
9138         /**
9139          * @event beforeselect
9140          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9141          * @param {Roo.View} this
9142          * @param {HTMLElement} node The node to be selected
9143          * @param {Array} selections Array of currently selected nodes
9144          */
9145             "beforeselect" : true,
9146         /**
9147          * @event preparedata
9148          * Fires on every row to render, to allow you to change the data.
9149          * @param {Roo.View} this
9150          * @param {Object} data to be rendered (change this)
9151          */
9152           "preparedata" : true
9153           
9154           
9155         });
9156
9157
9158
9159     this.el.on({
9160         "click": this.onClick,
9161         "dblclick": this.onDblClick,
9162         "contextmenu": this.onContextMenu,
9163         scope:this
9164     });
9165
9166     this.selections = [];
9167     this.nodes = [];
9168     this.cmp = new Roo.CompositeElementLite([]);
9169     if(this.store){
9170         this.store = Roo.factory(this.store, Roo.data);
9171         this.setStore(this.store, true);
9172     }
9173     
9174     if ( this.footer && this.footer.xtype) {
9175            
9176          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9177         
9178         this.footer.dataSource = this.store
9179         this.footer.container = fctr;
9180         this.footer = Roo.factory(this.footer, Roo);
9181         fctr.insertFirst(this.el);
9182         
9183         // this is a bit insane - as the paging toolbar seems to detach the el..
9184 //        dom.parentNode.parentNode.parentNode
9185          // they get detached?
9186     }
9187     
9188     
9189     Roo.View.superclass.constructor.call(this);
9190     
9191     
9192 };
9193
9194 Roo.extend(Roo.View, Roo.util.Observable, {
9195     
9196      /**
9197      * @cfg {Roo.data.Store} store Data store to load data from.
9198      */
9199     store : false,
9200     
9201     /**
9202      * @cfg {String|Roo.Element} el The container element.
9203      */
9204     el : '',
9205     
9206     /**
9207      * @cfg {String|Roo.Template} tpl The template used by this View 
9208      */
9209     tpl : false,
9210     /**
9211      * @cfg {String} dataName the named area of the template to use as the data area
9212      *                          Works with domtemplates roo-name="name"
9213      */
9214     dataName: false,
9215     /**
9216      * @cfg {String} selectedClass The css class to add to selected nodes
9217      */
9218     selectedClass : "x-view-selected",
9219      /**
9220      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9221      */
9222     emptyText : "",
9223     
9224     /**
9225      * @cfg {String} text to display on mask (default Loading)
9226      */
9227     mask : false,
9228     /**
9229      * @cfg {Boolean} multiSelect Allow multiple selection
9230      */
9231     multiSelect : false,
9232     /**
9233      * @cfg {Boolean} singleSelect Allow single selection
9234      */
9235     singleSelect:  false,
9236     
9237     /**
9238      * @cfg {Boolean} toggleSelect - selecting 
9239      */
9240     toggleSelect : false,
9241     
9242     /**
9243      * Returns the element this view is bound to.
9244      * @return {Roo.Element}
9245      */
9246     getEl : function(){
9247         return this.wrapEl;
9248     },
9249     
9250     
9251
9252     /**
9253      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9254      */
9255     refresh : function(){
9256         Roo.log('refresh');
9257         var t = this.tpl;
9258         
9259         // if we are using something like 'domtemplate', then
9260         // the what gets used is:
9261         // t.applySubtemplate(NAME, data, wrapping data..)
9262         // the outer template then get' applied with
9263         //     the store 'extra data'
9264         // and the body get's added to the
9265         //      roo-name="data" node?
9266         //      <span class='roo-tpl-{name}'></span> ?????
9267         
9268         
9269         
9270         this.clearSelections();
9271         this.el.update("");
9272         var html = [];
9273         var records = this.store.getRange();
9274         if(records.length < 1) {
9275             
9276             // is this valid??  = should it render a template??
9277             
9278             this.el.update(this.emptyText);
9279             return;
9280         }
9281         var el = this.el;
9282         if (this.dataName) {
9283             this.el.update(t.apply(this.store.meta)); //????
9284             el = this.el.child('.roo-tpl-' + this.dataName);
9285         }
9286         
9287         for(var i = 0, len = records.length; i < len; i++){
9288             var data = this.prepareData(records[i].data, i, records[i]);
9289             this.fireEvent("preparedata", this, data, i, records[i]);
9290             html[html.length] = Roo.util.Format.trim(
9291                 this.dataName ?
9292                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9293                     t.apply(data)
9294             );
9295         }
9296         
9297         
9298         
9299         el.update(html.join(""));
9300         this.nodes = el.dom.childNodes;
9301         this.updateIndexes(0);
9302     },
9303     
9304
9305     /**
9306      * Function to override to reformat the data that is sent to
9307      * the template for each node.
9308      * DEPRICATED - use the preparedata event handler.
9309      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9310      * a JSON object for an UpdateManager bound view).
9311      */
9312     prepareData : function(data, index, record)
9313     {
9314         this.fireEvent("preparedata", this, data, index, record);
9315         return data;
9316     },
9317
9318     onUpdate : function(ds, record){
9319          Roo.log('on update');   
9320         this.clearSelections();
9321         var index = this.store.indexOf(record);
9322         var n = this.nodes[index];
9323         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9324         n.parentNode.removeChild(n);
9325         this.updateIndexes(index, index);
9326     },
9327
9328     
9329     
9330 // --------- FIXME     
9331     onAdd : function(ds, records, index)
9332     {
9333         Roo.log(['on Add', ds, records, index] );        
9334         this.clearSelections();
9335         if(this.nodes.length == 0){
9336             this.refresh();
9337             return;
9338         }
9339         var n = this.nodes[index];
9340         for(var i = 0, len = records.length; i < len; i++){
9341             var d = this.prepareData(records[i].data, i, records[i]);
9342             if(n){
9343                 this.tpl.insertBefore(n, d);
9344             }else{
9345                 
9346                 this.tpl.append(this.el, d);
9347             }
9348         }
9349         this.updateIndexes(index);
9350     },
9351
9352     onRemove : function(ds, record, index){
9353         Roo.log('onRemove');
9354         this.clearSelections();
9355         var el = this.dataName  ?
9356             this.el.child('.roo-tpl-' + this.dataName) :
9357             this.el; 
9358         
9359         el.dom.removeChild(this.nodes[index]);
9360         this.updateIndexes(index);
9361     },
9362
9363     /**
9364      * Refresh an individual node.
9365      * @param {Number} index
9366      */
9367     refreshNode : function(index){
9368         this.onUpdate(this.store, this.store.getAt(index));
9369     },
9370
9371     updateIndexes : function(startIndex, endIndex){
9372         var ns = this.nodes;
9373         startIndex = startIndex || 0;
9374         endIndex = endIndex || ns.length - 1;
9375         for(var i = startIndex; i <= endIndex; i++){
9376             ns[i].nodeIndex = i;
9377         }
9378     },
9379
9380     /**
9381      * Changes the data store this view uses and refresh the view.
9382      * @param {Store} store
9383      */
9384     setStore : function(store, initial){
9385         if(!initial && this.store){
9386             this.store.un("datachanged", this.refresh);
9387             this.store.un("add", this.onAdd);
9388             this.store.un("remove", this.onRemove);
9389             this.store.un("update", this.onUpdate);
9390             this.store.un("clear", this.refresh);
9391             this.store.un("beforeload", this.onBeforeLoad);
9392             this.store.un("load", this.onLoad);
9393             this.store.un("loadexception", this.onLoad);
9394         }
9395         if(store){
9396           
9397             store.on("datachanged", this.refresh, this);
9398             store.on("add", this.onAdd, this);
9399             store.on("remove", this.onRemove, this);
9400             store.on("update", this.onUpdate, this);
9401             store.on("clear", this.refresh, this);
9402             store.on("beforeload", this.onBeforeLoad, this);
9403             store.on("load", this.onLoad, this);
9404             store.on("loadexception", this.onLoad, this);
9405         }
9406         
9407         if(store){
9408             this.refresh();
9409         }
9410     },
9411     /**
9412      * onbeforeLoad - masks the loading area.
9413      *
9414      */
9415     onBeforeLoad : function(store,opts)
9416     {
9417          Roo.log('onBeforeLoad');   
9418         if (!opts.add) {
9419             this.el.update("");
9420         }
9421         this.el.mask(this.mask ? this.mask : "Loading" ); 
9422     },
9423     onLoad : function ()
9424     {
9425         this.el.unmask();
9426     },
9427     
9428
9429     /**
9430      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9431      * @param {HTMLElement} node
9432      * @return {HTMLElement} The template node
9433      */
9434     findItemFromChild : function(node){
9435         var el = this.dataName  ?
9436             this.el.child('.roo-tpl-' + this.dataName,true) :
9437             this.el.dom; 
9438         
9439         if(!node || node.parentNode == el){
9440                     return node;
9441             }
9442             var p = node.parentNode;
9443             while(p && p != el){
9444             if(p.parentNode == el){
9445                 return p;
9446             }
9447             p = p.parentNode;
9448         }
9449             return null;
9450     },
9451
9452     /** @ignore */
9453     onClick : function(e){
9454         var item = this.findItemFromChild(e.getTarget());
9455         if(item){
9456             var index = this.indexOf(item);
9457             if(this.onItemClick(item, index, e) !== false){
9458                 this.fireEvent("click", this, index, item, e);
9459             }
9460         }else{
9461             this.clearSelections();
9462         }
9463     },
9464
9465     /** @ignore */
9466     onContextMenu : function(e){
9467         var item = this.findItemFromChild(e.getTarget());
9468         if(item){
9469             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9470         }
9471     },
9472
9473     /** @ignore */
9474     onDblClick : function(e){
9475         var item = this.findItemFromChild(e.getTarget());
9476         if(item){
9477             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9478         }
9479     },
9480
9481     onItemClick : function(item, index, e)
9482     {
9483         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9484             return false;
9485         }
9486         if (this.toggleSelect) {
9487             var m = this.isSelected(item) ? 'unselect' : 'select';
9488             Roo.log(m);
9489             var _t = this;
9490             _t[m](item, true, false);
9491             return true;
9492         }
9493         if(this.multiSelect || this.singleSelect){
9494             if(this.multiSelect && e.shiftKey && this.lastSelection){
9495                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9496             }else{
9497                 this.select(item, this.multiSelect && e.ctrlKey);
9498                 this.lastSelection = item;
9499             }
9500             e.preventDefault();
9501         }
9502         return true;
9503     },
9504
9505     /**
9506      * Get the number of selected nodes.
9507      * @return {Number}
9508      */
9509     getSelectionCount : function(){
9510         return this.selections.length;
9511     },
9512
9513     /**
9514      * Get the currently selected nodes.
9515      * @return {Array} An array of HTMLElements
9516      */
9517     getSelectedNodes : function(){
9518         return this.selections;
9519     },
9520
9521     /**
9522      * Get the indexes of the selected nodes.
9523      * @return {Array}
9524      */
9525     getSelectedIndexes : function(){
9526         var indexes = [], s = this.selections;
9527         for(var i = 0, len = s.length; i < len; i++){
9528             indexes.push(s[i].nodeIndex);
9529         }
9530         return indexes;
9531     },
9532
9533     /**
9534      * Clear all selections
9535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9536      */
9537     clearSelections : function(suppressEvent){
9538         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9539             this.cmp.elements = this.selections;
9540             this.cmp.removeClass(this.selectedClass);
9541             this.selections = [];
9542             if(!suppressEvent){
9543                 this.fireEvent("selectionchange", this, this.selections);
9544             }
9545         }
9546     },
9547
9548     /**
9549      * Returns true if the passed node is selected
9550      * @param {HTMLElement/Number} node The node or node index
9551      * @return {Boolean}
9552      */
9553     isSelected : function(node){
9554         var s = this.selections;
9555         if(s.length < 1){
9556             return false;
9557         }
9558         node = this.getNode(node);
9559         return s.indexOf(node) !== -1;
9560     },
9561
9562     /**
9563      * Selects nodes.
9564      * @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
9565      * @param {Boolean} keepExisting (optional) true to keep existing selections
9566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9567      */
9568     select : function(nodeInfo, keepExisting, suppressEvent){
9569         if(nodeInfo instanceof Array){
9570             if(!keepExisting){
9571                 this.clearSelections(true);
9572             }
9573             for(var i = 0, len = nodeInfo.length; i < len; i++){
9574                 this.select(nodeInfo[i], true, true);
9575             }
9576             return;
9577         } 
9578         var node = this.getNode(nodeInfo);
9579         if(!node || this.isSelected(node)){
9580             return; // already selected.
9581         }
9582         if(!keepExisting){
9583             this.clearSelections(true);
9584         }
9585         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9586             Roo.fly(node).addClass(this.selectedClass);
9587             this.selections.push(node);
9588             if(!suppressEvent){
9589                 this.fireEvent("selectionchange", this, this.selections);
9590             }
9591         }
9592         
9593         
9594     },
9595       /**
9596      * Unselects nodes.
9597      * @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
9598      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9599      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9600      */
9601     unselect : function(nodeInfo, keepExisting, suppressEvent)
9602     {
9603         if(nodeInfo instanceof Array){
9604             Roo.each(this.selections, function(s) {
9605                 this.unselect(s, nodeInfo);
9606             }, this);
9607             return;
9608         }
9609         var node = this.getNode(nodeInfo);
9610         if(!node || !this.isSelected(node)){
9611             Roo.log("not selected");
9612             return; // not selected.
9613         }
9614         // fireevent???
9615         var ns = [];
9616         Roo.each(this.selections, function(s) {
9617             if (s == node ) {
9618                 Roo.fly(node).removeClass(this.selectedClass);
9619
9620                 return;
9621             }
9622             ns.push(s);
9623         },this);
9624         
9625         this.selections= ns;
9626         this.fireEvent("selectionchange", this, this.selections);
9627     },
9628
9629     /**
9630      * Gets a template node.
9631      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9632      * @return {HTMLElement} The node or null if it wasn't found
9633      */
9634     getNode : function(nodeInfo){
9635         if(typeof nodeInfo == "string"){
9636             return document.getElementById(nodeInfo);
9637         }else if(typeof nodeInfo == "number"){
9638             return this.nodes[nodeInfo];
9639         }
9640         return nodeInfo;
9641     },
9642
9643     /**
9644      * Gets a range template nodes.
9645      * @param {Number} startIndex
9646      * @param {Number} endIndex
9647      * @return {Array} An array of nodes
9648      */
9649     getNodes : function(start, end){
9650         var ns = this.nodes;
9651         start = start || 0;
9652         end = typeof end == "undefined" ? ns.length - 1 : end;
9653         var nodes = [];
9654         if(start <= end){
9655             for(var i = start; i <= end; i++){
9656                 nodes.push(ns[i]);
9657             }
9658         } else{
9659             for(var i = start; i >= end; i--){
9660                 nodes.push(ns[i]);
9661             }
9662         }
9663         return nodes;
9664     },
9665
9666     /**
9667      * Finds the index of the passed node
9668      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9669      * @return {Number} The index of the node or -1
9670      */
9671     indexOf : function(node){
9672         node = this.getNode(node);
9673         if(typeof node.nodeIndex == "number"){
9674             return node.nodeIndex;
9675         }
9676         var ns = this.nodes;
9677         for(var i = 0, len = ns.length; i < len; i++){
9678             if(ns[i] == node){
9679                 return i;
9680             }
9681         }
9682         return -1;
9683     }
9684 });
9685 /*
9686  * - LGPL
9687  *
9688  * based on jquery fullcalendar
9689  * 
9690  */
9691
9692 Roo.bootstrap = Roo.bootstrap || {};
9693 /**
9694  * @class Roo.bootstrap.Calendar
9695  * @extends Roo.bootstrap.Component
9696  * Bootstrap Calendar class
9697  * @cfg {Boolean} loadMask (true|false) default false
9698     
9699  * @constructor
9700  * Create a new Container
9701  * @param {Object} config The config object
9702  */
9703
9704
9705
9706 Roo.bootstrap.Calendar = function(config){
9707     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9708      this.addEvents({
9709         /**
9710              * @event select
9711              * Fires when a date is selected
9712              * @param {DatePicker} this
9713              * @param {Date} date The selected date
9714              */
9715         'select': true,
9716         /**
9717              * @event monthchange
9718              * Fires when the displayed month changes 
9719              * @param {DatePicker} this
9720              * @param {Date} date The selected month
9721              */
9722         'monthchange': true,
9723         /**
9724              * @event evententer
9725              * Fires when mouse over an event
9726              * @param {Calendar} this
9727              * @param {event} Event
9728              */
9729         'evententer': true,
9730         /**
9731              * @event eventleave
9732              * Fires when the mouse leaves an
9733              * @param {Calendar} this
9734              * @param {event}
9735              */
9736         'eventleave': true,
9737         /**
9738              * @event eventclick
9739              * Fires when the mouse click an
9740              * @param {Calendar} this
9741              * @param {event}
9742              */
9743         'eventclick': true
9744         
9745     });
9746
9747 };
9748
9749 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9750     
9751      /**
9752      * @cfg {Number} startDay
9753      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9754      */
9755     startDay : 0,
9756     
9757     loadMask : false,
9758       
9759     getAutoCreate : function(){
9760         
9761         
9762         var fc_button = function(name, corner, style, content ) {
9763             return Roo.apply({},{
9764                 tag : 'span',
9765                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9766                          (corner.length ?
9767                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9768                             ''
9769                         ),
9770                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9771                 unselectable: 'on'
9772             });
9773         };
9774         
9775         var header = {
9776             tag : 'table',
9777             cls : 'fc-header',
9778             style : 'width:100%',
9779             cn : [
9780                 {
9781                     tag: 'tr',
9782                     cn : [
9783                         {
9784                             tag : 'td',
9785                             cls : 'fc-header-left',
9786                             cn : [
9787                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9788                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9789                                 { tag: 'span', cls: 'fc-header-space' },
9790                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9791                                 
9792                                 
9793                             ]
9794                         },
9795                         
9796                         {
9797                             tag : 'td',
9798                             cls : 'fc-header-center',
9799                             cn : [
9800                                 {
9801                                     tag: 'span',
9802                                     cls: 'fc-header-title',
9803                                     cn : {
9804                                         tag: 'H2',
9805                                         html : 'month / year'
9806                                     }
9807                                 }
9808                                 
9809                             ]
9810                         },
9811                         {
9812                             tag : 'td',
9813                             cls : 'fc-header-right',
9814                             cn : [
9815                           /*      fc_button('month', 'left', '', 'month' ),
9816                                 fc_button('week', '', '', 'week' ),
9817                                 fc_button('day', 'right', '', 'day' )
9818                             */    
9819                                 
9820                             ]
9821                         }
9822                         
9823                     ]
9824                 }
9825             ]
9826         };
9827         
9828        
9829         var cal_heads = function() {
9830             var ret = [];
9831             // fixme - handle this.
9832             
9833             for (var i =0; i < Date.dayNames.length; i++) {
9834                 var d = Date.dayNames[i];
9835                 ret.push({
9836                     tag: 'th',
9837                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9838                     html : d.substring(0,3)
9839                 });
9840                 
9841             }
9842             ret[0].cls += ' fc-first';
9843             ret[6].cls += ' fc-last';
9844             return ret;
9845         };
9846         var cal_cell = function(n) {
9847             return  {
9848                 tag: 'td',
9849                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9850                 cn : [
9851                     {
9852                         cn : [
9853                             {
9854                                 cls: 'fc-day-number',
9855                                 html: 'D'
9856                             },
9857                             {
9858                                 cls: 'fc-day-content',
9859                              
9860                                 cn : [
9861                                      {
9862                                         style: 'position: relative;' // height: 17px;
9863                                     }
9864                                 ]
9865                             }
9866                             
9867                             
9868                         ]
9869                     }
9870                 ]
9871                 
9872             }
9873         };
9874         var cal_rows = function() {
9875             
9876             var ret = []
9877             for (var r = 0; r < 6; r++) {
9878                 var row= {
9879                     tag : 'tr',
9880                     cls : 'fc-week',
9881                     cn : []
9882                 };
9883                 
9884                 for (var i =0; i < Date.dayNames.length; i++) {
9885                     var d = Date.dayNames[i];
9886                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9887
9888                 }
9889                 row.cn[0].cls+=' fc-first';
9890                 row.cn[0].cn[0].style = 'min-height:90px';
9891                 row.cn[6].cls+=' fc-last';
9892                 ret.push(row);
9893                 
9894             }
9895             ret[0].cls += ' fc-first';
9896             ret[4].cls += ' fc-prev-last';
9897             ret[5].cls += ' fc-last';
9898             return ret;
9899             
9900         };
9901         
9902         var cal_table = {
9903             tag: 'table',
9904             cls: 'fc-border-separate',
9905             style : 'width:100%',
9906             cellspacing  : 0,
9907             cn : [
9908                 { 
9909                     tag: 'thead',
9910                     cn : [
9911                         { 
9912                             tag: 'tr',
9913                             cls : 'fc-first fc-last',
9914                             cn : cal_heads()
9915                         }
9916                     ]
9917                 },
9918                 { 
9919                     tag: 'tbody',
9920                     cn : cal_rows()
9921                 }
9922                   
9923             ]
9924         };
9925          
9926          var cfg = {
9927             cls : 'fc fc-ltr',
9928             cn : [
9929                 header,
9930                 {
9931                     cls : 'fc-content',
9932                     style : "position: relative;",
9933                     cn : [
9934                         {
9935                             cls : 'fc-view fc-view-month fc-grid',
9936                             style : 'position: relative',
9937                             unselectable : 'on',
9938                             cn : [
9939                                 {
9940                                     cls : 'fc-event-container',
9941                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9942                                 },
9943                                 cal_table
9944                             ]
9945                         }
9946                     ]
9947     
9948                 }
9949            ] 
9950             
9951         };
9952         
9953          
9954         
9955         return cfg;
9956     },
9957     
9958     
9959     initEvents : function()
9960     {
9961         if(!this.store){
9962             throw "can not find store for calendar";
9963         }
9964         
9965         var mark = {
9966             tag: "div",
9967             cls:"x-dlg-mask",
9968             style: "text-align:center",
9969             cn: [
9970                 {
9971                     tag: "div",
9972                     style: "background-color:white;width:50%;margin:250 auto",
9973                     cn: [
9974                         {
9975                             tag: "img",
9976                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9977                         },
9978                         {
9979                             tag: "span",
9980                             html: "Loading"
9981                         }
9982                         
9983                     ]
9984                 }
9985             ]
9986         }
9987         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9988         
9989         var size = this.el.select('.fc-content', true).first().getSize();
9990         this.maskEl.setSize(size.width, size.height);
9991         this.maskEl.enableDisplayMode("block");
9992         if(!this.loadMask){
9993             this.maskEl.hide();
9994         }
9995         
9996         this.store = Roo.factory(this.store, Roo.data);
9997         this.store.on('load', this.onLoad, this);
9998         this.store.on('beforeload', this.onBeforeLoad, this);
9999         
10000         this.resize();
10001         
10002         this.cells = this.el.select('.fc-day',true);
10003         //Roo.log(this.cells);
10004         this.textNodes = this.el.query('.fc-day-number');
10005         this.cells.addClassOnOver('fc-state-hover');
10006         
10007         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10008         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10009         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10010         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10011         
10012         this.on('monthchange', this.onMonthChange, this);
10013         
10014         this.update(new Date().clearTime());
10015     },
10016     
10017     resize : function() {
10018         var sz  = this.el.getSize();
10019         
10020         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10021         this.el.select('.fc-day-content div',true).setHeight(34);
10022     },
10023     
10024     
10025     // private
10026     showPrevMonth : function(e){
10027         this.update(this.activeDate.add("mo", -1));
10028     },
10029     showToday : function(e){
10030         this.update(new Date().clearTime());
10031     },
10032     // private
10033     showNextMonth : function(e){
10034         this.update(this.activeDate.add("mo", 1));
10035     },
10036
10037     // private
10038     showPrevYear : function(){
10039         this.update(this.activeDate.add("y", -1));
10040     },
10041
10042     // private
10043     showNextYear : function(){
10044         this.update(this.activeDate.add("y", 1));
10045     },
10046
10047     
10048    // private
10049     update : function(date)
10050     {
10051         var vd = this.activeDate;
10052         this.activeDate = date;
10053 //        if(vd && this.el){
10054 //            var t = date.getTime();
10055 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10056 //                Roo.log('using add remove');
10057 //                
10058 //                this.fireEvent('monthchange', this, date);
10059 //                
10060 //                this.cells.removeClass("fc-state-highlight");
10061 //                this.cells.each(function(c){
10062 //                   if(c.dateValue == t){
10063 //                       c.addClass("fc-state-highlight");
10064 //                       setTimeout(function(){
10065 //                            try{c.dom.firstChild.focus();}catch(e){}
10066 //                       }, 50);
10067 //                       return false;
10068 //                   }
10069 //                   return true;
10070 //                });
10071 //                return;
10072 //            }
10073 //        }
10074         
10075         var days = date.getDaysInMonth();
10076         
10077         var firstOfMonth = date.getFirstDateOfMonth();
10078         var startingPos = firstOfMonth.getDay()-this.startDay;
10079         
10080         if(startingPos < this.startDay){
10081             startingPos += 7;
10082         }
10083         
10084         var pm = date.add(Date.MONTH, -1);
10085         var prevStart = pm.getDaysInMonth()-startingPos;
10086 //        
10087         this.cells = this.el.select('.fc-day',true);
10088         this.textNodes = this.el.query('.fc-day-number');
10089         this.cells.addClassOnOver('fc-state-hover');
10090         
10091         var cells = this.cells.elements;
10092         var textEls = this.textNodes;
10093         
10094         Roo.each(cells, function(cell){
10095             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10096         });
10097         
10098         days += startingPos;
10099
10100         // convert everything to numbers so it's fast
10101         var day = 86400000;
10102         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10103         //Roo.log(d);
10104         //Roo.log(pm);
10105         //Roo.log(prevStart);
10106         
10107         var today = new Date().clearTime().getTime();
10108         var sel = date.clearTime().getTime();
10109         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10110         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10111         var ddMatch = this.disabledDatesRE;
10112         var ddText = this.disabledDatesText;
10113         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10114         var ddaysText = this.disabledDaysText;
10115         var format = this.format;
10116         
10117         var setCellClass = function(cal, cell){
10118             
10119             //Roo.log('set Cell Class');
10120             cell.title = "";
10121             var t = d.getTime();
10122             
10123             //Roo.log(d);
10124             
10125             cell.dateValue = t;
10126             if(t == today){
10127                 cell.className += " fc-today";
10128                 cell.className += " fc-state-highlight";
10129                 cell.title = cal.todayText;
10130             }
10131             if(t == sel){
10132                 // disable highlight in other month..
10133                 //cell.className += " fc-state-highlight";
10134                 
10135             }
10136             // disabling
10137             if(t < min) {
10138                 cell.className = " fc-state-disabled";
10139                 cell.title = cal.minText;
10140                 return;
10141             }
10142             if(t > max) {
10143                 cell.className = " fc-state-disabled";
10144                 cell.title = cal.maxText;
10145                 return;
10146             }
10147             if(ddays){
10148                 if(ddays.indexOf(d.getDay()) != -1){
10149                     cell.title = ddaysText;
10150                     cell.className = " fc-state-disabled";
10151                 }
10152             }
10153             if(ddMatch && format){
10154                 var fvalue = d.dateFormat(format);
10155                 if(ddMatch.test(fvalue)){
10156                     cell.title = ddText.replace("%0", fvalue);
10157                     cell.className = " fc-state-disabled";
10158                 }
10159             }
10160             
10161             if (!cell.initialClassName) {
10162                 cell.initialClassName = cell.dom.className;
10163             }
10164             
10165             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10166         };
10167
10168         var i = 0;
10169         
10170         for(; i < startingPos; i++) {
10171             textEls[i].innerHTML = (++prevStart);
10172             d.setDate(d.getDate()+1);
10173             
10174             cells[i].className = "fc-past fc-other-month";
10175             setCellClass(this, cells[i]);
10176         }
10177         
10178         var intDay = 0;
10179         
10180         for(; i < days; i++){
10181             intDay = i - startingPos + 1;
10182             textEls[i].innerHTML = (intDay);
10183             d.setDate(d.getDate()+1);
10184             
10185             cells[i].className = ''; // "x-date-active";
10186             setCellClass(this, cells[i]);
10187         }
10188         var extraDays = 0;
10189         
10190         for(; i < 42; i++) {
10191             textEls[i].innerHTML = (++extraDays);
10192             d.setDate(d.getDate()+1);
10193             
10194             cells[i].className = "fc-future fc-other-month";
10195             setCellClass(this, cells[i]);
10196         }
10197         
10198         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10199         
10200         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10201         
10202         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10203         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10204         
10205         if(totalRows != 6){
10206             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10207             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10208         }
10209         
10210         this.fireEvent('monthchange', this, date);
10211         
10212         
10213         /*
10214         if(!this.internalRender){
10215             var main = this.el.dom.firstChild;
10216             var w = main.offsetWidth;
10217             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10218             Roo.fly(main).setWidth(w);
10219             this.internalRender = true;
10220             // opera does not respect the auto grow header center column
10221             // then, after it gets a width opera refuses to recalculate
10222             // without a second pass
10223             if(Roo.isOpera && !this.secondPass){
10224                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10225                 this.secondPass = true;
10226                 this.update.defer(10, this, [date]);
10227             }
10228         }
10229         */
10230         
10231     },
10232     
10233     findCell : function(dt) {
10234         dt = dt.clearTime().getTime();
10235         var ret = false;
10236         this.cells.each(function(c){
10237             //Roo.log("check " +c.dateValue + '?=' + dt);
10238             if(c.dateValue == dt){
10239                 ret = c;
10240                 return false;
10241             }
10242             return true;
10243         });
10244         
10245         return ret;
10246     },
10247     
10248     findCells : function(ev) {
10249         var s = ev.start.clone().clearTime().getTime();
10250        // Roo.log(s);
10251         var e= ev.end.clone().clearTime().getTime();
10252        // Roo.log(e);
10253         var ret = [];
10254         this.cells.each(function(c){
10255              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10256             
10257             if(c.dateValue > e){
10258                 return ;
10259             }
10260             if(c.dateValue < s){
10261                 return ;
10262             }
10263             ret.push(c);
10264         });
10265         
10266         return ret;    
10267     },
10268     
10269     findBestRow: function(cells)
10270     {
10271         var ret = 0;
10272         
10273         for (var i =0 ; i < cells.length;i++) {
10274             ret  = Math.max(cells[i].rows || 0,ret);
10275         }
10276         return ret;
10277         
10278     },
10279     
10280     
10281     addItem : function(ev)
10282     {
10283         // look for vertical location slot in
10284         var cells = this.findCells(ev);
10285         
10286         ev.row = this.findBestRow(cells);
10287         
10288         // work out the location.
10289         
10290         var crow = false;
10291         var rows = [];
10292         for(var i =0; i < cells.length; i++) {
10293             if (!crow) {
10294                 crow = {
10295                     start : cells[i],
10296                     end :  cells[i]
10297                 };
10298                 continue;
10299             }
10300             if (crow.start.getY() == cells[i].getY()) {
10301                 // on same row.
10302                 crow.end = cells[i];
10303                 continue;
10304             }
10305             // different row.
10306             rows.push(crow);
10307             crow = {
10308                 start: cells[i],
10309                 end : cells[i]
10310             };
10311             
10312         }
10313         
10314         rows.push(crow);
10315         ev.els = [];
10316         ev.rows = rows;
10317         ev.cells = cells;
10318         for (var i = 0; i < cells.length;i++) {
10319             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10320             
10321         }
10322         
10323         this.calevents.push(ev);
10324     },
10325     
10326     clearEvents: function() {
10327         
10328         if(!this.calevents){
10329             return;
10330         }
10331         
10332         Roo.each(this.cells.elements, function(c){
10333             c.rows = 0;
10334         });
10335         
10336         Roo.each(this.calevents, function(e) {
10337             Roo.each(e.els, function(el) {
10338                 el.un('mouseenter' ,this.onEventEnter, this);
10339                 el.un('mouseleave' ,this.onEventLeave, this);
10340                 el.remove();
10341             },this);
10342         },this);
10343         
10344     },
10345     
10346     renderEvents: function()
10347     {   
10348         // first make sure there is enough space..
10349         
10350         this.cells.each(function(c) {
10351         
10352             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10353         });
10354         
10355         for (var e = 0; e < this.calevents.length; e++) {
10356             var ev = this.calevents[e];
10357             var cells = ev.cells;
10358             var rows = ev.rows;
10359             
10360             for(var i =0; i < rows.length; i++) {
10361                 
10362                  
10363                 // how many rows should it span..
10364                 
10365                 var  cfg = {
10366                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10367                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10368                     
10369                     unselectable : "on",
10370                     cn : [
10371                         {
10372                             cls: 'fc-event-inner',
10373                             cn : [
10374 //                                {
10375 //                                  tag:'span',
10376 //                                  cls: 'fc-event-time',
10377 //                                  html : cells.length > 1 ? '' : ev.time
10378 //                                },
10379                                 {
10380                                   tag:'span',
10381                                   cls: 'fc-event-title',
10382                                   html : String.format('{0}', ev.title)
10383                                 }
10384                                 
10385                                 
10386                             ]
10387                         },
10388                         {
10389                             cls: 'ui-resizable-handle ui-resizable-e',
10390                             html : '&nbsp;&nbsp;&nbsp'
10391                         }
10392                         
10393                     ]
10394                 };
10395                 if (i == 0) {
10396                     cfg.cls += ' fc-event-start';
10397                 }
10398                 if ((i+1) == rows.length) {
10399                     cfg.cls += ' fc-event-end';
10400                 }
10401                 
10402                 var ctr = this.el.select('.fc-event-container',true).first();
10403                 var cg = ctr.createChild(cfg);
10404                 
10405                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10406                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10407                 cg.on('click', this.onEventClick, this, ev);
10408                 
10409                 ev.els.push(cg);
10410                 
10411                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10412                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10413                 //Roo.log(cg);
10414                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10415                 cg.setWidth(ebox.right - sbox.x -2);
10416             }
10417             
10418             
10419         }
10420         
10421     },
10422     
10423     onEventEnter: function (e, el,event,d) {
10424         this.fireEvent('evententer', this, el, event);
10425     },
10426     
10427     onEventLeave: function (e, el,event,d) {
10428         this.fireEvent('eventleave', this, el, event);
10429     },
10430     
10431     onEventClick: function (e, el,event,d) {
10432         this.fireEvent('eventclick', this, el, event);
10433     },
10434     
10435     onMonthChange: function () {
10436         this.store.load();
10437     },
10438     
10439     onLoad: function () 
10440     {   
10441         this.calevents = [];
10442         var cal = this;
10443         
10444         if(this.store.getCount() > 0){
10445             this.store.data.each(function(d){
10446                cal.addItem({
10447                     id : d.data.id,
10448                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10449                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10450                     time : d.data.start_time,
10451                     title : d.data.title,
10452                     description : d.data.description,
10453                     venue : d.data.venue
10454                 });
10455             });
10456         }
10457         
10458         this.renderEvents();
10459         
10460         if(this.loadMask){
10461             this.maskEl.hide();
10462         }
10463     },
10464     
10465     onBeforeLoad: function()
10466     {
10467         this.clearEvents();
10468         
10469         if(this.loadMask){
10470             this.maskEl.show();
10471         }
10472     }
10473 });
10474
10475  
10476  /*
10477  * - LGPL
10478  *
10479  * element
10480  * 
10481  */
10482
10483 /**
10484  * @class Roo.bootstrap.Popover
10485  * @extends Roo.bootstrap.Component
10486  * Bootstrap Popover class
10487  * @cfg {String} html contents of the popover   (or false to use children..)
10488  * @cfg {String} title of popover (or false to hide)
10489  * @cfg {String} placement how it is placed
10490  * @cfg {String} trigger click || hover (or false to trigger manually)
10491  * @cfg {String} over what (parent or false to trigger manually.)
10492  * 
10493  * @constructor
10494  * Create a new Popover
10495  * @param {Object} config The config object
10496  */
10497
10498 Roo.bootstrap.Popover = function(config){
10499     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10500 };
10501
10502 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10503     
10504     title: 'Fill in a title',
10505     html: false,
10506     
10507     placement : 'right',
10508     trigger : 'hover', // hover
10509     
10510     over: 'parent',
10511     
10512     can_build_overlaid : false,
10513     
10514     getChildContainer : function()
10515     {
10516         return this.el.select('.popover-content',true).first();
10517     },
10518     
10519     getAutoCreate : function(){
10520          Roo.log('make popover?');
10521         var cfg = {
10522            cls : 'popover roo-dynamic',
10523            style: 'display:block',
10524            cn : [
10525                 {
10526                     cls : 'arrow'
10527                 },
10528                 {
10529                     cls : 'popover-inner',
10530                     cn : [
10531                         {
10532                             tag: 'h3',
10533                             cls: 'popover-title',
10534                             html : this.title
10535                         },
10536                         {
10537                             cls : 'popover-content',
10538                             html : this.html
10539                         }
10540                     ]
10541                     
10542                 }
10543            ]
10544         };
10545         
10546         return cfg;
10547     },
10548     setTitle: function(str)
10549     {
10550         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10551     },
10552     setContent: function(str)
10553     {
10554         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10555     },
10556     // as it get's added to the bottom of the page.
10557     onRender : function(ct, position)
10558     {
10559         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10560         if(!this.el){
10561             var cfg = Roo.apply({},  this.getAutoCreate());
10562             cfg.id = Roo.id();
10563             
10564             if (this.cls) {
10565                 cfg.cls += ' ' + this.cls;
10566             }
10567             if (this.style) {
10568                 cfg.style = this.style;
10569             }
10570             Roo.log("adding to ")
10571             this.el = Roo.get(document.body).createChild(cfg, position);
10572             Roo.log(this.el);
10573         }
10574         this.initEvents();
10575     },
10576     
10577     initEvents : function()
10578     {
10579         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10580         this.el.enableDisplayMode('block');
10581         this.el.hide();
10582         if (this.over === false) {
10583             return; 
10584         }
10585         if (this.triggers === false) {
10586             return;
10587         }
10588         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10589         var triggers = this.trigger ? this.trigger.split(' ') : [];
10590         Roo.each(triggers, function(trigger) {
10591         
10592             if (trigger == 'click') {
10593                 on_el.on('click', this.toggle, this);
10594             } else if (trigger != 'manual') {
10595                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10596                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10597       
10598                 on_el.on(eventIn  ,this.enter, this);
10599                 on_el.on(eventOut, this.leave, this);
10600             }
10601         }, this);
10602         
10603     },
10604     
10605     
10606     // private
10607     timeout : null,
10608     hoverState : null,
10609     
10610     toggle : function () {
10611         this.hoverState == 'in' ? this.leave() : this.enter();
10612     },
10613     
10614     enter : function () {
10615        
10616     
10617         clearTimeout(this.timeout);
10618     
10619         this.hoverState = 'in'
10620     
10621         if (!this.delay || !this.delay.show) {
10622             this.show();
10623             return 
10624         }
10625         var _t = this;
10626         this.timeout = setTimeout(function () {
10627             if (_t.hoverState == 'in') {
10628                 _t.show();
10629             }
10630         }, this.delay.show)
10631     },
10632     leave : function() {
10633         clearTimeout(this.timeout);
10634     
10635         this.hoverState = 'out'
10636     
10637         if (!this.delay || !this.delay.hide) {
10638             this.hide();
10639             return 
10640         }
10641         var _t = this;
10642         this.timeout = setTimeout(function () {
10643             if (_t.hoverState == 'out') {
10644                 _t.hide();
10645             }
10646         }, this.delay.hide)
10647     },
10648     
10649     show : function (on_el)
10650     {
10651         if (!on_el) {
10652             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10653         }
10654         // set content.
10655         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10656         if (this.html !== false) {
10657             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10658         }
10659         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10660         if (!this.title.length) {
10661             this.el.select('.popover-title',true).hide();
10662         }
10663         
10664         var placement = typeof this.placement == 'function' ?
10665             this.placement.call(this, this.el, on_el) :
10666             this.placement;
10667             
10668         var autoToken = /\s?auto?\s?/i;
10669         var autoPlace = autoToken.test(placement);
10670         if (autoPlace) {
10671             placement = placement.replace(autoToken, '') || 'top';
10672         }
10673         
10674         //this.el.detach()
10675         //this.el.setXY([0,0]);
10676         this.el.show();
10677         this.el.dom.style.display='block';
10678         this.el.addClass(placement);
10679         
10680         //this.el.appendTo(on_el);
10681         
10682         var p = this.getPosition();
10683         var box = this.el.getBox();
10684         
10685         if (autoPlace) {
10686             // fixme..
10687         }
10688         var align = Roo.bootstrap.Popover.alignment[placement]
10689         this.el.alignTo(on_el, align[0],align[1]);
10690         //var arrow = this.el.select('.arrow',true).first();
10691         //arrow.set(align[2], 
10692         
10693         this.el.addClass('in');
10694         this.hoverState = null;
10695         
10696         if (this.el.hasClass('fade')) {
10697             // fade it?
10698         }
10699         
10700     },
10701     hide : function()
10702     {
10703         this.el.setXY([0,0]);
10704         this.el.removeClass('in');
10705         this.el.hide();
10706         
10707     }
10708     
10709 });
10710
10711 Roo.bootstrap.Popover.alignment = {
10712     'left' : ['r-l', [-10,0], 'right'],
10713     'right' : ['l-r', [10,0], 'left'],
10714     'bottom' : ['t-b', [0,10], 'top'],
10715     'top' : [ 'b-t', [0,-10], 'bottom']
10716 };
10717
10718  /*
10719  * - LGPL
10720  *
10721  * Progress
10722  * 
10723  */
10724
10725 /**
10726  * @class Roo.bootstrap.Progress
10727  * @extends Roo.bootstrap.Component
10728  * Bootstrap Progress class
10729  * @cfg {Boolean} striped striped of the progress bar
10730  * @cfg {Boolean} active animated of the progress bar
10731  * 
10732  * 
10733  * @constructor
10734  * Create a new Progress
10735  * @param {Object} config The config object
10736  */
10737
10738 Roo.bootstrap.Progress = function(config){
10739     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10740 };
10741
10742 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10743     
10744     striped : false,
10745     active: false,
10746     
10747     getAutoCreate : function(){
10748         var cfg = {
10749             tag: 'div',
10750             cls: 'progress'
10751         };
10752         
10753         
10754         if(this.striped){
10755             cfg.cls += ' progress-striped';
10756         }
10757       
10758         if(this.active){
10759             cfg.cls += ' active';
10760         }
10761         
10762         
10763         return cfg;
10764     }
10765    
10766 });
10767
10768  
10769
10770  /*
10771  * - LGPL
10772  *
10773  * ProgressBar
10774  * 
10775  */
10776
10777 /**
10778  * @class Roo.bootstrap.ProgressBar
10779  * @extends Roo.bootstrap.Component
10780  * Bootstrap ProgressBar class
10781  * @cfg {Number} aria_valuenow aria-value now
10782  * @cfg {Number} aria_valuemin aria-value min
10783  * @cfg {Number} aria_valuemax aria-value max
10784  * @cfg {String} label label for the progress bar
10785  * @cfg {String} panel (success | info | warning | danger )
10786  * @cfg {String} role role of the progress bar
10787  * @cfg {String} sr_only text
10788  * 
10789  * 
10790  * @constructor
10791  * Create a new ProgressBar
10792  * @param {Object} config The config object
10793  */
10794
10795 Roo.bootstrap.ProgressBar = function(config){
10796     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10797 };
10798
10799 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10800     
10801     aria_valuenow : 0,
10802     aria_valuemin : 0,
10803     aria_valuemax : 100,
10804     label : false,
10805     panel : false,
10806     role : false,
10807     sr_only: false,
10808     
10809     getAutoCreate : function()
10810     {
10811         
10812         var cfg = {
10813             tag: 'div',
10814             cls: 'progress-bar',
10815             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10816         };
10817         
10818         if(this.sr_only){
10819             cfg.cn = {
10820                 tag: 'span',
10821                 cls: 'sr-only',
10822                 html: this.sr_only
10823             }
10824         }
10825         
10826         if(this.role){
10827             cfg.role = this.role;
10828         }
10829         
10830         if(this.aria_valuenow){
10831             cfg['aria-valuenow'] = this.aria_valuenow;
10832         }
10833         
10834         if(this.aria_valuemin){
10835             cfg['aria-valuemin'] = this.aria_valuemin;
10836         }
10837         
10838         if(this.aria_valuemax){
10839             cfg['aria-valuemax'] = this.aria_valuemax;
10840         }
10841         
10842         if(this.label && !this.sr_only){
10843             cfg.html = this.label;
10844         }
10845         
10846         if(this.panel){
10847             cfg.cls += ' progress-bar-' + this.panel;
10848         }
10849         
10850         return cfg;
10851     },
10852     
10853     update : function(aria_valuenow)
10854     {
10855         this.aria_valuenow = aria_valuenow;
10856         
10857         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10858     }
10859    
10860 });
10861
10862  
10863
10864  /*
10865  * - LGPL
10866  *
10867  * TabPanel
10868  * 
10869  */
10870
10871 /**
10872  * @class Roo.bootstrap.TabPanel
10873  * @extends Roo.bootstrap.Component
10874  * Bootstrap TabPanel class
10875  * @cfg {Boolean} active panel active
10876  * @cfg {String} html panel content
10877  * @cfg {String} tabId tab relate id
10878  * 
10879  * 
10880  * @constructor
10881  * Create a new TabPanel
10882  * @param {Object} config The config object
10883  */
10884
10885 Roo.bootstrap.TabPanel = function(config){
10886     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10887 };
10888
10889 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10890     
10891     active: false,
10892     html: false,
10893     tabId: false,
10894     
10895     getAutoCreate : function(){
10896         var cfg = {
10897             tag: 'div',
10898             cls: 'tab-pane',
10899             html: this.html || ''
10900         };
10901         
10902         if(this.active){
10903             cfg.cls += ' active';
10904         }
10905         
10906         if(this.tabId){
10907             cfg.tabId = this.tabId;
10908         }
10909         
10910         return cfg;
10911     }
10912    
10913 });
10914
10915  
10916
10917  /*
10918  * - LGPL
10919  *
10920  * DateField
10921  * 
10922  */
10923
10924 /**
10925  * @class Roo.bootstrap.DateField
10926  * @extends Roo.bootstrap.Input
10927  * Bootstrap DateField class
10928  * @cfg {Number} weekStart default 0
10929  * @cfg {Number} weekStart default 0
10930  * @cfg {Number} viewMode default empty, (months|years)
10931  * @cfg {Number} minViewMode default empty, (months|years)
10932  * @cfg {Number} startDate default -Infinity
10933  * @cfg {Number} endDate default Infinity
10934  * @cfg {Boolean} todayHighlight default false
10935  * @cfg {Boolean} todayBtn default false
10936  * @cfg {Boolean} calendarWeeks default false
10937  * @cfg {Object} daysOfWeekDisabled default empty
10938  * 
10939  * @cfg {Boolean} keyboardNavigation default true
10940  * @cfg {String} language default en
10941  * 
10942  * @constructor
10943  * Create a new DateField
10944  * @param {Object} config The config object
10945  */
10946
10947 Roo.bootstrap.DateField = function(config){
10948     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10949      this.addEvents({
10950             /**
10951              * @event show
10952              * Fires when this field show.
10953              * @param {Roo.bootstrap.DateField} this
10954              * @param {Mixed} date The date value
10955              */
10956             show : true,
10957             /**
10958              * @event show
10959              * Fires when this field hide.
10960              * @param {Roo.bootstrap.DateField} this
10961              * @param {Mixed} date The date value
10962              */
10963             hide : true,
10964             /**
10965              * @event select
10966              * Fires when select a date.
10967              * @param {Roo.bootstrap.DateField} this
10968              * @param {Mixed} date The date value
10969              */
10970             select : true
10971         });
10972 };
10973
10974 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10975     
10976     /**
10977      * @cfg {String} format
10978      * The default date format string which can be overriden for localization support.  The format must be
10979      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10980      */
10981     format : "m/d/y",
10982     /**
10983      * @cfg {String} altFormats
10984      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10985      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10986      */
10987     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10988     
10989     weekStart : 0,
10990     
10991     viewMode : '',
10992     
10993     minViewMode : '',
10994     
10995     todayHighlight : false,
10996     
10997     todayBtn: false,
10998     
10999     language: 'en',
11000     
11001     keyboardNavigation: true,
11002     
11003     calendarWeeks: false,
11004     
11005     startDate: -Infinity,
11006     
11007     endDate: Infinity,
11008     
11009     daysOfWeekDisabled: [],
11010     
11011     _events: [],
11012     
11013     UTCDate: function()
11014     {
11015         return new Date(Date.UTC.apply(Date, arguments));
11016     },
11017     
11018     UTCToday: function()
11019     {
11020         var today = new Date();
11021         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11022     },
11023     
11024     getDate: function() {
11025             var d = this.getUTCDate();
11026             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11027     },
11028     
11029     getUTCDate: function() {
11030             return this.date;
11031     },
11032     
11033     setDate: function(d) {
11034             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11035     },
11036     
11037     setUTCDate: function(d) {
11038             this.date = d;
11039             this.setValue(this.formatDate(this.date));
11040     },
11041         
11042     onRender: function(ct, position)
11043     {
11044         
11045         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11046         
11047         this.language = this.language || 'en';
11048         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11049         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11050         
11051         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11052         this.format = this.format || 'm/d/y';
11053         this.isInline = false;
11054         this.isInput = true;
11055         this.component = this.el.select('.add-on', true).first() || false;
11056         this.component = (this.component && this.component.length === 0) ? false : this.component;
11057         this.hasInput = this.component && this.inputEL().length;
11058         
11059         if (typeof(this.minViewMode === 'string')) {
11060             switch (this.minViewMode) {
11061                 case 'months':
11062                     this.minViewMode = 1;
11063                     break;
11064                 case 'years':
11065                     this.minViewMode = 2;
11066                     break;
11067                 default:
11068                     this.minViewMode = 0;
11069                     break;
11070             }
11071         }
11072         
11073         if (typeof(this.viewMode === 'string')) {
11074             switch (this.viewMode) {
11075                 case 'months':
11076                     this.viewMode = 1;
11077                     break;
11078                 case 'years':
11079                     this.viewMode = 2;
11080                     break;
11081                 default:
11082                     this.viewMode = 0;
11083                     break;
11084             }
11085         }
11086                 
11087         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11088         
11089         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11090         
11091         this.picker().on('mousedown', this.onMousedown, this);
11092         this.picker().on('click', this.onClick, this);
11093         
11094         this.picker().addClass('datepicker-dropdown');
11095         
11096         this.startViewMode = this.viewMode;
11097         
11098         
11099         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11100             if(!this.calendarWeeks){
11101                 v.remove();
11102                 return;
11103             };
11104             
11105             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11106             v.attr('colspan', function(i, val){
11107                 return parseInt(val) + 1;
11108             });
11109         })
11110                         
11111         
11112         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11113         
11114         this.setStartDate(this.startDate);
11115         this.setEndDate(this.endDate);
11116         
11117         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11118         
11119         this.fillDow();
11120         this.fillMonths();
11121         this.update();
11122         this.showMode();
11123         
11124         if(this.isInline) {
11125             this.show();
11126         }
11127     },
11128     
11129     picker : function()
11130     {
11131         return this.el.select('.datepicker', true).first();
11132     },
11133     
11134     fillDow: function()
11135     {
11136         var dowCnt = this.weekStart;
11137         
11138         var dow = {
11139             tag: 'tr',
11140             cn: [
11141                 
11142             ]
11143         };
11144         
11145         if(this.calendarWeeks){
11146             dow.cn.push({
11147                 tag: 'th',
11148                 cls: 'cw',
11149                 html: '&nbsp;'
11150             })
11151         }
11152         
11153         while (dowCnt < this.weekStart + 7) {
11154             dow.cn.push({
11155                 tag: 'th',
11156                 cls: 'dow',
11157                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11158             });
11159         }
11160         
11161         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11162     },
11163     
11164     fillMonths: function()
11165     {    
11166         var i = 0
11167         var months = this.picker().select('>.datepicker-months td', true).first();
11168         
11169         months.dom.innerHTML = '';
11170         
11171         while (i < 12) {
11172             var month = {
11173                 tag: 'span',
11174                 cls: 'month',
11175                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11176             }
11177             
11178             months.createChild(month);
11179         }
11180         
11181     },
11182     
11183     update: function(){
11184         
11185         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11186         
11187         if (this.date < this.startDate) {
11188             this.viewDate = new Date(this.startDate);
11189         } else if (this.date > this.endDate) {
11190             this.viewDate = new Date(this.endDate);
11191         } else {
11192             this.viewDate = new Date(this.date);
11193         }
11194         
11195         this.fill();
11196     },
11197     
11198     fill: function() {
11199         var d = new Date(this.viewDate),
11200                 year = d.getUTCFullYear(),
11201                 month = d.getUTCMonth(),
11202                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11203                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11204                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11205                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11206                 currentDate = this.date && this.date.valueOf(),
11207                 today = this.UTCToday();
11208         
11209         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11210         
11211 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11212         
11213 //        this.picker.select('>tfoot th.today').
11214 //                                              .text(dates[this.language].today)
11215 //                                              .toggle(this.todayBtn !== false);
11216     
11217         this.updateNavArrows();
11218         this.fillMonths();
11219                                                 
11220         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11221         
11222         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11223          
11224         prevMonth.setUTCDate(day);
11225         
11226         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11227         
11228         var nextMonth = new Date(prevMonth);
11229         
11230         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11231         
11232         nextMonth = nextMonth.valueOf();
11233         
11234         var fillMonths = false;
11235         
11236         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11237         
11238         while(prevMonth.valueOf() < nextMonth) {
11239             var clsName = '';
11240             
11241             if (prevMonth.getUTCDay() === this.weekStart) {
11242                 if(fillMonths){
11243                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11244                 }
11245                     
11246                 fillMonths = {
11247                     tag: 'tr',
11248                     cn: []
11249                 };
11250                 
11251                 if(this.calendarWeeks){
11252                     // ISO 8601: First week contains first thursday.
11253                     // ISO also states week starts on Monday, but we can be more abstract here.
11254                     var
11255                     // Start of current week: based on weekstart/current date
11256                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11257                     // Thursday of this week
11258                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11259                     // First Thursday of year, year from thursday
11260                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11261                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11262                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11263                     
11264                     fillMonths.cn.push({
11265                         tag: 'td',
11266                         cls: 'cw',
11267                         html: calWeek
11268                     });
11269                 }
11270             }
11271             
11272             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11273                 clsName += ' old';
11274             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11275                 clsName += ' new';
11276             }
11277             if (this.todayHighlight &&
11278                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11279                 prevMonth.getUTCMonth() == today.getMonth() &&
11280                 prevMonth.getUTCDate() == today.getDate()) {
11281                 clsName += ' today';
11282             }
11283             
11284             if (currentDate && prevMonth.valueOf() === currentDate) {
11285                 clsName += ' active';
11286             }
11287             
11288             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11289                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11290                     clsName += ' disabled';
11291             }
11292             
11293             fillMonths.cn.push({
11294                 tag: 'td',
11295                 cls: 'day ' + clsName,
11296                 html: prevMonth.getDate()
11297             })
11298             
11299             prevMonth.setDate(prevMonth.getDate()+1);
11300         }
11301           
11302         var currentYear = this.date && this.date.getUTCFullYear();
11303         var currentMonth = this.date && this.date.getUTCMonth();
11304         
11305         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11306         
11307         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11308             v.removeClass('active');
11309             
11310             if(currentYear === year && k === currentMonth){
11311                 v.addClass('active');
11312             }
11313             
11314             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11315                 v.addClass('disabled');
11316             }
11317             
11318         });
11319         
11320         
11321         year = parseInt(year/10, 10) * 10;
11322         
11323         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11324         
11325         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11326         
11327         year -= 1;
11328         for (var i = -1; i < 11; i++) {
11329             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11330                 tag: 'span',
11331                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11332                 html: year
11333             })
11334             
11335             year += 1;
11336         }
11337     },
11338     
11339     showMode: function(dir) {
11340         if (dir) {
11341             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11342         }
11343         Roo.each(this.picker().select('>div',true).elements, function(v){
11344             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11345             v.hide();
11346         });
11347         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11348     },
11349     
11350     place: function()
11351     {
11352         if(this.isInline) return;
11353         
11354         this.picker().removeClass(['bottom', 'top']);
11355         
11356         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11357             /*
11358              * place to the top of element!
11359              *
11360              */
11361             
11362             this.picker().addClass('top');
11363             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11364             
11365             return;
11366         }
11367         
11368         this.picker().addClass('bottom');
11369         
11370         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11371     },
11372     
11373     parseDate : function(value){
11374         if(!value || value instanceof Date){
11375             return value;
11376         }
11377         var v = Date.parseDate(value, this.format);
11378         if (!v && this.useIso) {
11379             v = Date.parseDate(value, 'Y-m-d');
11380         }
11381         if(!v && this.altFormats){
11382             if(!this.altFormatsArray){
11383                 this.altFormatsArray = this.altFormats.split("|");
11384             }
11385             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11386                 v = Date.parseDate(value, this.altFormatsArray[i]);
11387             }
11388         }
11389         return v;
11390     },
11391     
11392     formatDate : function(date, fmt){
11393         return (!date || !(date instanceof Date)) ?
11394         date : date.dateFormat(fmt || this.format);
11395     },
11396     
11397     onFocus : function()
11398     {
11399         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11400         this.show();
11401     },
11402     
11403     onBlur : function()
11404     {
11405         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11406         this.hide();
11407     },
11408     
11409     show : function()
11410     {
11411         this.picker().show();
11412         this.update();
11413         this.place();
11414         
11415         this.fireEvent('show', this, this.date);
11416     },
11417     
11418     hide : function()
11419     {
11420         if(this.isInline) return;
11421         this.picker().hide();
11422         this.viewMode = this.startViewMode;
11423         this.showMode();
11424         
11425         this.fireEvent('hide', this, this.date);
11426         
11427     },
11428     
11429     onMousedown: function(e){
11430         e.stopPropagation();
11431         e.preventDefault();
11432     },
11433     
11434     keyup: function(e){
11435         Roo.bootstrap.DateField.superclass.keyup.call(this);
11436         this.update();
11437         
11438     },
11439
11440     setValue: function(v){
11441         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11442         
11443         this.fireEvent('select', this, this.date);
11444         
11445     },
11446     
11447     fireKey: function(e){
11448         if (!this.picker().isVisible()){
11449             if (e.keyCode == 27) // allow escape to hide and re-show picker
11450                 this.show();
11451             return;
11452         }
11453         var dateChanged = false,
11454         dir, day, month,
11455         newDate, newViewDate;
11456         switch(e.keyCode){
11457             case 27: // escape
11458                 this.hide();
11459                 e.preventDefault();
11460                 break;
11461             case 37: // left
11462             case 39: // right
11463                 if (!this.keyboardNavigation) break;
11464                 dir = e.keyCode == 37 ? -1 : 1;
11465                 
11466                 if (e.ctrlKey){
11467                     newDate = this.moveYear(this.date, dir);
11468                     newViewDate = this.moveYear(this.viewDate, dir);
11469                 } else if (e.shiftKey){
11470                     newDate = this.moveMonth(this.date, dir);
11471                     newViewDate = this.moveMonth(this.viewDate, dir);
11472                 } else {
11473                     newDate = new Date(this.date);
11474                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11475                     newViewDate = new Date(this.viewDate);
11476                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11477                 }
11478                 if (this.dateWithinRange(newDate)){
11479                     this.date = newDate;
11480                     this.viewDate = newViewDate;
11481                     this.setValue(this.formatDate(this.date));
11482                     this.update();
11483                     e.preventDefault();
11484                     dateChanged = true;
11485                 }
11486                 break;
11487             case 38: // up
11488             case 40: // down
11489                 if (!this.keyboardNavigation) break;
11490                 dir = e.keyCode == 38 ? -1 : 1;
11491                 if (e.ctrlKey){
11492                     newDate = this.moveYear(this.date, dir);
11493                     newViewDate = this.moveYear(this.viewDate, dir);
11494                 } else if (e.shiftKey){
11495                     newDate = this.moveMonth(this.date, dir);
11496                     newViewDate = this.moveMonth(this.viewDate, dir);
11497                 } else {
11498                     newDate = new Date(this.date);
11499                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11500                     newViewDate = new Date(this.viewDate);
11501                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11502                 }
11503                 if (this.dateWithinRange(newDate)){
11504                     this.date = newDate;
11505                     this.viewDate = newViewDate;
11506                     this.setValue(this.formatDate(this.date));
11507                     this.update();
11508                     e.preventDefault();
11509                     dateChanged = true;
11510                 }
11511                 break;
11512             case 13: // enter
11513                 this.setValue(this.formatDate(this.date));
11514                 this.hide();
11515                 e.preventDefault();
11516                 break;
11517             case 9: // tab
11518                 this.setValue(this.formatDate(this.date));
11519                 this.hide();
11520                 break;
11521         }
11522     },
11523     
11524     
11525     onClick: function(e) {
11526         e.stopPropagation();
11527         e.preventDefault();
11528         
11529         var target = e.getTarget();
11530         
11531         if(target.nodeName.toLowerCase() === 'i'){
11532             target = Roo.get(target).dom.parentNode;
11533         }
11534         
11535         var nodeName = target.nodeName;
11536         var className = target.className;
11537         var html = target.innerHTML;
11538         
11539         switch(nodeName.toLowerCase()) {
11540             case 'th':
11541                 switch(className) {
11542                     case 'switch':
11543                         this.showMode(1);
11544                         break;
11545                     case 'prev':
11546                     case 'next':
11547                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11548                         switch(this.viewMode){
11549                                 case 0:
11550                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11551                                         break;
11552                                 case 1:
11553                                 case 2:
11554                                         this.viewDate = this.moveYear(this.viewDate, dir);
11555                                         break;
11556                         }
11557                         this.fill();
11558                         break;
11559                     case 'today':
11560                         var date = new Date();
11561                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11562                         this.fill()
11563                         this.setValue(this.formatDate(this.date));
11564                         this.hide();
11565                         break;
11566                 }
11567                 break;
11568             case 'span':
11569                 if (className.indexOf('disabled') === -1) {
11570                     this.viewDate.setUTCDate(1);
11571                     if (className.indexOf('month') !== -1) {
11572                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11573                     } else {
11574                         var year = parseInt(html, 10) || 0;
11575                         this.viewDate.setUTCFullYear(year);
11576                         
11577                     }
11578                     this.showMode(-1);
11579                     this.fill();
11580                 }
11581                 break;
11582                 
11583             case 'td':
11584                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11585                     var day = parseInt(html, 10) || 1;
11586                     var year = this.viewDate.getUTCFullYear(),
11587                         month = this.viewDate.getUTCMonth();
11588
11589                     if (className.indexOf('old') !== -1) {
11590                         if(month === 0 ){
11591                             month = 11;
11592                             year -= 1;
11593                         }else{
11594                             month -= 1;
11595                         }
11596                     } else if (className.indexOf('new') !== -1) {
11597                         if (month == 11) {
11598                             month = 0;
11599                             year += 1;
11600                         } else {
11601                             month += 1;
11602                         }
11603                     }
11604                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11605                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11606                     this.fill();
11607                     this.setValue(this.formatDate(this.date));
11608                     this.hide();
11609                 }
11610                 break;
11611         }
11612     },
11613     
11614     setStartDate: function(startDate){
11615         this.startDate = startDate || -Infinity;
11616         if (this.startDate !== -Infinity) {
11617             this.startDate = this.parseDate(this.startDate);
11618         }
11619         this.update();
11620         this.updateNavArrows();
11621     },
11622
11623     setEndDate: function(endDate){
11624         this.endDate = endDate || Infinity;
11625         if (this.endDate !== Infinity) {
11626             this.endDate = this.parseDate(this.endDate);
11627         }
11628         this.update();
11629         this.updateNavArrows();
11630     },
11631     
11632     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11633         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11634         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11635             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11636         }
11637         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11638             return parseInt(d, 10);
11639         });
11640         this.update();
11641         this.updateNavArrows();
11642     },
11643     
11644     updateNavArrows: function() {
11645         var d = new Date(this.viewDate),
11646         year = d.getUTCFullYear(),
11647         month = d.getUTCMonth();
11648         
11649         Roo.each(this.picker().select('.prev', true).elements, function(v){
11650             v.show();
11651             switch (this.viewMode) {
11652                 case 0:
11653
11654                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11655                         v.hide();
11656                     }
11657                     break;
11658                 case 1:
11659                 case 2:
11660                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11661                         v.hide();
11662                     }
11663                     break;
11664             }
11665         });
11666         
11667         Roo.each(this.picker().select('.next', true).elements, function(v){
11668             v.show();
11669             switch (this.viewMode) {
11670                 case 0:
11671
11672                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11673                         v.hide();
11674                     }
11675                     break;
11676                 case 1:
11677                 case 2:
11678                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11679                         v.hide();
11680                     }
11681                     break;
11682             }
11683         })
11684     },
11685     
11686     moveMonth: function(date, dir){
11687         if (!dir) return date;
11688         var new_date = new Date(date.valueOf()),
11689         day = new_date.getUTCDate(),
11690         month = new_date.getUTCMonth(),
11691         mag = Math.abs(dir),
11692         new_month, test;
11693         dir = dir > 0 ? 1 : -1;
11694         if (mag == 1){
11695             test = dir == -1
11696             // If going back one month, make sure month is not current month
11697             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11698             ? function(){
11699                 return new_date.getUTCMonth() == month;
11700             }
11701             // If going forward one month, make sure month is as expected
11702             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11703             : function(){
11704                 return new_date.getUTCMonth() != new_month;
11705             };
11706             new_month = month + dir;
11707             new_date.setUTCMonth(new_month);
11708             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11709             if (new_month < 0 || new_month > 11)
11710                 new_month = (new_month + 12) % 12;
11711         } else {
11712             // For magnitudes >1, move one month at a time...
11713             for (var i=0; i<mag; i++)
11714                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11715                 new_date = this.moveMonth(new_date, dir);
11716             // ...then reset the day, keeping it in the new month
11717             new_month = new_date.getUTCMonth();
11718             new_date.setUTCDate(day);
11719             test = function(){
11720                 return new_month != new_date.getUTCMonth();
11721             };
11722         }
11723         // Common date-resetting loop -- if date is beyond end of month, make it
11724         // end of month
11725         while (test()){
11726             new_date.setUTCDate(--day);
11727             new_date.setUTCMonth(new_month);
11728         }
11729         return new_date;
11730     },
11731
11732     moveYear: function(date, dir){
11733         return this.moveMonth(date, dir*12);
11734     },
11735
11736     dateWithinRange: function(date){
11737         return date >= this.startDate && date <= this.endDate;
11738     },
11739
11740     
11741     remove: function() {
11742         this.picker().remove();
11743     }
11744    
11745 });
11746
11747 Roo.apply(Roo.bootstrap.DateField,  {
11748     
11749     head : {
11750         tag: 'thead',
11751         cn: [
11752         {
11753             tag: 'tr',
11754             cn: [
11755             {
11756                 tag: 'th',
11757                 cls: 'prev',
11758                 html: '<i class="icon-arrow-left"/>'
11759             },
11760             {
11761                 tag: 'th',
11762                 cls: 'switch',
11763                 colspan: '5'
11764             },
11765             {
11766                 tag: 'th',
11767                 cls: 'next',
11768                 html: '<i class="icon-arrow-right"/>'
11769             }
11770
11771             ]
11772         }
11773         ]
11774     },
11775     
11776     content : {
11777         tag: 'tbody',
11778         cn: [
11779         {
11780             tag: 'tr',
11781             cn: [
11782             {
11783                 tag: 'td',
11784                 colspan: '7'
11785             }
11786             ]
11787         }
11788         ]
11789     },
11790     
11791     footer : {
11792         tag: 'tfoot',
11793         cn: [
11794         {
11795             tag: 'tr',
11796             cn: [
11797             {
11798                 tag: 'th',
11799                 colspan: '7',
11800                 cls: 'today'
11801             }
11802                     
11803             ]
11804         }
11805         ]
11806     },
11807     
11808     dates:{
11809         en: {
11810             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11811             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11812             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11813             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11814             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11815             today: "Today"
11816         }
11817     },
11818     
11819     modes: [
11820     {
11821         clsName: 'days',
11822         navFnc: 'Month',
11823         navStep: 1
11824     },
11825     {
11826         clsName: 'months',
11827         navFnc: 'FullYear',
11828         navStep: 1
11829     },
11830     {
11831         clsName: 'years',
11832         navFnc: 'FullYear',
11833         navStep: 10
11834     }]
11835 });
11836
11837 Roo.apply(Roo.bootstrap.DateField,  {
11838   
11839     template : {
11840         tag: 'div',
11841         cls: 'datepicker dropdown-menu',
11842         cn: [
11843         {
11844             tag: 'div',
11845             cls: 'datepicker-days',
11846             cn: [
11847             {
11848                 tag: 'table',
11849                 cls: 'table-condensed',
11850                 cn:[
11851                 Roo.bootstrap.DateField.head,
11852                 {
11853                     tag: 'tbody'
11854                 },
11855                 Roo.bootstrap.DateField.footer
11856                 ]
11857             }
11858             ]
11859         },
11860         {
11861             tag: 'div',
11862             cls: 'datepicker-months',
11863             cn: [
11864             {
11865                 tag: 'table',
11866                 cls: 'table-condensed',
11867                 cn:[
11868                 Roo.bootstrap.DateField.head,
11869                 Roo.bootstrap.DateField.content,
11870                 Roo.bootstrap.DateField.footer
11871                 ]
11872             }
11873             ]
11874         },
11875         {
11876             tag: 'div',
11877             cls: 'datepicker-years',
11878             cn: [
11879             {
11880                 tag: 'table',
11881                 cls: 'table-condensed',
11882                 cn:[
11883                 Roo.bootstrap.DateField.head,
11884                 Roo.bootstrap.DateField.content,
11885                 Roo.bootstrap.DateField.footer
11886                 ]
11887             }
11888             ]
11889         }
11890         ]
11891     }
11892 });
11893
11894  
11895
11896  /*
11897  * - LGPL
11898  *
11899  * TimeField
11900  * 
11901  */
11902
11903 /**
11904  * @class Roo.bootstrap.TimeField
11905  * @extends Roo.bootstrap.Input
11906  * Bootstrap DateField class
11907  * 
11908  * 
11909  * @constructor
11910  * Create a new TimeField
11911  * @param {Object} config The config object
11912  */
11913
11914 Roo.bootstrap.TimeField = function(config){
11915     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11916     this.addEvents({
11917             /**
11918              * @event show
11919              * Fires when this field show.
11920              * @param {Roo.bootstrap.DateField} this
11921              * @param {Mixed} date The date value
11922              */
11923             show : true,
11924             /**
11925              * @event show
11926              * Fires when this field hide.
11927              * @param {Roo.bootstrap.DateField} this
11928              * @param {Mixed} date The date value
11929              */
11930             hide : true,
11931             /**
11932              * @event select
11933              * Fires when select a date.
11934              * @param {Roo.bootstrap.DateField} this
11935              * @param {Mixed} date The date value
11936              */
11937             select : true
11938         });
11939 };
11940
11941 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11942     
11943     /**
11944      * @cfg {String} format
11945      * The default time format string which can be overriden for localization support.  The format must be
11946      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11947      */
11948     format : "H:i",
11949        
11950     onRender: function(ct, position)
11951     {
11952         
11953         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11954                 
11955         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11956         
11957         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11958         
11959         this.pop = this.picker().select('>.datepicker-time',true).first();
11960         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11961         
11962         this.picker().on('mousedown', this.onMousedown, this);
11963         this.picker().on('click', this.onClick, this);
11964         
11965         this.picker().addClass('datepicker-dropdown');
11966     
11967         this.fillTime();
11968         this.update();
11969             
11970         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11971         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11972         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11973         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11974         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11975         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11976
11977     },
11978     
11979     fireKey: function(e){
11980         if (!this.picker().isVisible()){
11981             if (e.keyCode == 27) // allow escape to hide and re-show picker
11982                 this.show();
11983             return;
11984         }
11985
11986         e.preventDefault();
11987         
11988         switch(e.keyCode){
11989             case 27: // escape
11990                 this.hide();
11991                 break;
11992             case 37: // left
11993             case 39: // right
11994                 this.onTogglePeriod();
11995                 break;
11996             case 38: // up
11997                 this.onIncrementMinutes();
11998                 break;
11999             case 40: // down
12000                 this.onDecrementMinutes();
12001                 break;
12002             case 13: // enter
12003             case 9: // tab
12004                 this.setTime();
12005                 break;
12006         }
12007     },
12008     
12009     onClick: function(e) {
12010         e.stopPropagation();
12011         e.preventDefault();
12012     },
12013     
12014     picker : function()
12015     {
12016         return this.el.select('.datepicker', true).first();
12017     },
12018     
12019     fillTime: function()
12020     {    
12021         var time = this.pop.select('tbody', true).first();
12022         
12023         time.dom.innerHTML = '';
12024         
12025         time.createChild({
12026             tag: 'tr',
12027             cn: [
12028                 {
12029                     tag: 'td',
12030                     cn: [
12031                         {
12032                             tag: 'a',
12033                             href: '#',
12034                             cls: 'btn',
12035                             cn: [
12036                                 {
12037                                     tag: 'span',
12038                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12039                                 }
12040                             ]
12041                         } 
12042                     ]
12043                 },
12044                 {
12045                     tag: 'td',
12046                     cls: 'separator'
12047                 },
12048                 {
12049                     tag: 'td',
12050                     cn: [
12051                         {
12052                             tag: 'a',
12053                             href: '#',
12054                             cls: 'btn',
12055                             cn: [
12056                                 {
12057                                     tag: 'span',
12058                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12059                                 }
12060                             ]
12061                         }
12062                     ]
12063                 },
12064                 {
12065                     tag: 'td',
12066                     cls: 'separator'
12067                 }
12068             ]
12069         });
12070         
12071         time.createChild({
12072             tag: 'tr',
12073             cn: [
12074                 {
12075                     tag: 'td',
12076                     cn: [
12077                         {
12078                             tag: 'span',
12079                             cls: 'timepicker-hour',
12080                             html: '00'
12081                         }  
12082                     ]
12083                 },
12084                 {
12085                     tag: 'td',
12086                     cls: 'separator',
12087                     html: ':'
12088                 },
12089                 {
12090                     tag: 'td',
12091                     cn: [
12092                         {
12093                             tag: 'span',
12094                             cls: 'timepicker-minute',
12095                             html: '00'
12096                         }  
12097                     ]
12098                 },
12099                 {
12100                     tag: 'td',
12101                     cls: 'separator'
12102                 },
12103                 {
12104                     tag: 'td',
12105                     cn: [
12106                         {
12107                             tag: 'button',
12108                             type: 'button',
12109                             cls: 'btn btn-primary period',
12110                             html: 'AM'
12111                             
12112                         }
12113                     ]
12114                 }
12115             ]
12116         });
12117         
12118         time.createChild({
12119             tag: 'tr',
12120             cn: [
12121                 {
12122                     tag: 'td',
12123                     cn: [
12124                         {
12125                             tag: 'a',
12126                             href: '#',
12127                             cls: 'btn',
12128                             cn: [
12129                                 {
12130                                     tag: 'span',
12131                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12132                                 }
12133                             ]
12134                         }
12135                     ]
12136                 },
12137                 {
12138                     tag: 'td',
12139                     cls: 'separator'
12140                 },
12141                 {
12142                     tag: 'td',
12143                     cn: [
12144                         {
12145                             tag: 'a',
12146                             href: '#',
12147                             cls: 'btn',
12148                             cn: [
12149                                 {
12150                                     tag: 'span',
12151                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12152                                 }
12153                             ]
12154                         }
12155                     ]
12156                 },
12157                 {
12158                     tag: 'td',
12159                     cls: 'separator'
12160                 }
12161             ]
12162         });
12163         
12164     },
12165     
12166     update: function()
12167     {
12168         
12169         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12170         
12171         this.fill();
12172     },
12173     
12174     fill: function() 
12175     {
12176         var hours = this.time.getHours();
12177         var minutes = this.time.getMinutes();
12178         var period = 'AM';
12179         
12180         if(hours > 11){
12181             period = 'PM';
12182         }
12183         
12184         if(hours == 0){
12185             hours = 12;
12186         }
12187         
12188         
12189         if(hours > 12){
12190             hours = hours - 12;
12191         }
12192         
12193         if(hours < 10){
12194             hours = '0' + hours;
12195         }
12196         
12197         if(minutes < 10){
12198             minutes = '0' + minutes;
12199         }
12200         
12201         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12202         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12203         this.pop.select('button', true).first().dom.innerHTML = period;
12204         
12205     },
12206     
12207     place: function()
12208     {   
12209         this.picker().removeClass(['bottom', 'top']);
12210         
12211         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12212             /*
12213              * place to the top of element!
12214              *
12215              */
12216             
12217             this.picker().addClass('top');
12218             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12219             
12220             return;
12221         }
12222         
12223         this.picker().addClass('bottom');
12224         
12225         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12226     },
12227   
12228     onFocus : function()
12229     {
12230         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12231         this.show();
12232     },
12233     
12234     onBlur : function()
12235     {
12236         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12237         this.hide();
12238     },
12239     
12240     show : function()
12241     {
12242         this.picker().show();
12243         this.pop.show();
12244         this.update();
12245         this.place();
12246         
12247         this.fireEvent('show', this, this.date);
12248     },
12249     
12250     hide : function()
12251     {
12252         this.picker().hide();
12253         this.pop.hide();
12254         
12255         this.fireEvent('hide', this, this.date);
12256     },
12257     
12258     setTime : function()
12259     {
12260         this.hide();
12261         this.setValue(this.time.format(this.format));
12262         
12263         this.fireEvent('select', this, this.date);
12264         
12265         
12266     },
12267     
12268     onMousedown: function(e){
12269         e.stopPropagation();
12270         e.preventDefault();
12271     },
12272     
12273     onIncrementHours: function()
12274     {
12275         Roo.log('onIncrementHours');
12276         this.time = this.time.add(Date.HOUR, 1);
12277         this.update();
12278         
12279     },
12280     
12281     onDecrementHours: function()
12282     {
12283         Roo.log('onDecrementHours');
12284         this.time = this.time.add(Date.HOUR, -1);
12285         this.update();
12286     },
12287     
12288     onIncrementMinutes: function()
12289     {
12290         Roo.log('onIncrementMinutes');
12291         this.time = this.time.add(Date.MINUTE, 1);
12292         this.update();
12293     },
12294     
12295     onDecrementMinutes: function()
12296     {
12297         Roo.log('onDecrementMinutes');
12298         this.time = this.time.add(Date.MINUTE, -1);
12299         this.update();
12300     },
12301     
12302     onTogglePeriod: function()
12303     {
12304         Roo.log('onTogglePeriod');
12305         this.time = this.time.add(Date.HOUR, 12);
12306         this.update();
12307     }
12308     
12309    
12310 });
12311
12312 Roo.apply(Roo.bootstrap.TimeField,  {
12313     
12314     content : {
12315         tag: 'tbody',
12316         cn: [
12317             {
12318                 tag: 'tr',
12319                 cn: [
12320                 {
12321                     tag: 'td',
12322                     colspan: '7'
12323                 }
12324                 ]
12325             }
12326         ]
12327     },
12328     
12329     footer : {
12330         tag: 'tfoot',
12331         cn: [
12332             {
12333                 tag: 'tr',
12334                 cn: [
12335                 {
12336                     tag: 'th',
12337                     colspan: '7',
12338                     cls: '',
12339                     cn: [
12340                         {
12341                             tag: 'button',
12342                             cls: 'btn btn-info ok',
12343                             html: 'OK'
12344                         }
12345                     ]
12346                 }
12347
12348                 ]
12349             }
12350         ]
12351     }
12352 });
12353
12354 Roo.apply(Roo.bootstrap.TimeField,  {
12355   
12356     template : {
12357         tag: 'div',
12358         cls: 'datepicker dropdown-menu',
12359         cn: [
12360             {
12361                 tag: 'div',
12362                 cls: 'datepicker-time',
12363                 cn: [
12364                 {
12365                     tag: 'table',
12366                     cls: 'table-condensed',
12367                     cn:[
12368                     Roo.bootstrap.TimeField.content,
12369                     Roo.bootstrap.TimeField.footer
12370                     ]
12371                 }
12372                 ]
12373             }
12374         ]
12375     }
12376 });
12377
12378  
12379
12380  /*
12381  * - LGPL
12382  *
12383  * CheckBox
12384  * 
12385  */
12386
12387 /**
12388  * @class Roo.bootstrap.CheckBox
12389  * @extends Roo.bootstrap.Input
12390  * Bootstrap CheckBox class
12391  * 
12392  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12393  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12394  * @cfg {String} boxLabel The text that appears beside the checkbox
12395  * @cfg {Boolean} checked initnal the element
12396  * 
12397  * @constructor
12398  * Create a new CheckBox
12399  * @param {Object} config The config object
12400  */
12401
12402 Roo.bootstrap.CheckBox = function(config){
12403     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12404    
12405         this.addEvents({
12406             /**
12407             * @event check
12408             * Fires when the element is checked or unchecked.
12409             * @param {Roo.bootstrap.CheckBox} this This input
12410             * @param {Boolean} checked The new checked value
12411             */
12412            check : true
12413         });
12414 };
12415
12416 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12417     
12418     inputType: 'checkbox',
12419     inputValue: 1,
12420     valueOff: 0,
12421     boxLabel: false,
12422     checked: false,
12423     
12424     getAutoCreate : function()
12425     {
12426         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12427         
12428         var id = Roo.id();
12429         
12430         var cfg = {};
12431         
12432         cfg.cls = 'form-group' //input-group
12433         
12434         var input =  {
12435             tag: 'input',
12436             id : id,
12437             type : this.inputType,
12438             value : (!this.checked) ? this.valueOff : this.inputValue,
12439             cls : 'form-box',
12440             placeholder : this.placeholder || ''
12441             
12442         };
12443         
12444         if (this.disabled) {
12445             input.disabled=true;
12446         }
12447         
12448         if(this.checked){
12449             input.checked = this.checked;
12450         }
12451         
12452         if (this.name) {
12453             input.name = this.name;
12454         }
12455         
12456         if (this.size) {
12457             input.cls += ' input-' + this.size;
12458         }
12459         
12460         var settings=this;
12461         ['xs','sm','md','lg'].map(function(size){
12462             if (settings[size]) {
12463                 cfg.cls += ' col-' + size + '-' + settings[size];
12464             }
12465         });
12466         
12467         var inputblock = input;
12468         
12469         if (this.before || this.after) {
12470             
12471             inputblock = {
12472                 cls : 'input-group',
12473                 cn :  [] 
12474             };
12475             if (this.before) {
12476                 inputblock.cn.push({
12477                     tag :'span',
12478                     cls : 'input-group-addon',
12479                     html : this.before
12480                 });
12481             }
12482             inputblock.cn.push(input);
12483             if (this.after) {
12484                 inputblock.cn.push({
12485                     tag :'span',
12486                     cls : 'input-group-addon',
12487                     html : this.after
12488                 });
12489             }
12490             
12491         };
12492         
12493         if (align ==='left' && this.fieldLabel.length) {
12494                 Roo.log("left and has label");
12495                 cfg.cn = [
12496                     
12497                     {
12498                         tag: 'label',
12499                         'for' :  id,
12500                         cls : 'control-label col-md-' + this.labelWidth,
12501                         html : this.fieldLabel
12502                         
12503                     },
12504                     {
12505                         cls : "col-md-" + (12 - this.labelWidth), 
12506                         cn: [
12507                             inputblock
12508                         ]
12509                     }
12510                     
12511                 ];
12512         } else if ( this.fieldLabel.length) {
12513                 Roo.log(" label");
12514                 cfg.cn = [
12515                    
12516                     {
12517                         tag: this.boxLabel ? 'span' : 'label',
12518                         'for': id,
12519                         cls: 'control-label box-input-label',
12520                         //cls : 'input-group-addon',
12521                         html : this.fieldLabel
12522                         
12523                     },
12524                     
12525                     inputblock
12526                     
12527                 ];
12528
12529         } else {
12530             
12531                    Roo.log(" no label && no align");
12532                 cfg.cn = [
12533                     
12534                         inputblock
12535                     
12536                 ];
12537                 
12538                 
12539         };
12540         
12541         if(this.boxLabel){
12542             cfg.cn.push({
12543                 tag: 'label',
12544                 'for': id,
12545                 cls: 'box-label',
12546                 html: this.boxLabel
12547             })
12548         }
12549         
12550         return cfg;
12551         
12552     },
12553     
12554     /**
12555      * return the real input element.
12556      */
12557     inputEl: function ()
12558     {
12559         return this.el.select('input.form-box',true).first();
12560     },
12561     
12562     initEvents : function()
12563     {
12564 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12565         
12566         this.inputEl().on('click', this.onClick,  this);
12567         
12568     },
12569     
12570     onClick : function()
12571     {   
12572         this.setChecked(!this.checked);
12573     },
12574     
12575     setChecked : function(state,suppressEvent)
12576     {
12577         this.checked = state;
12578         
12579         this.inputEl().dom.checked = state;
12580         
12581         if(suppressEvent !== true){
12582             this.fireEvent('check', this, state);
12583         }
12584         
12585         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12586         
12587     },
12588     
12589     setValue : function(v,suppressEvent)
12590     {
12591         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12592     }
12593     
12594 });
12595
12596  
12597 /*
12598  * - LGPL
12599  *
12600  * Radio
12601  * 
12602  */
12603
12604 /**
12605  * @class Roo.bootstrap.Radio
12606  * @extends Roo.bootstrap.CheckBox
12607  * Bootstrap Radio class
12608
12609  * @constructor
12610  * Create a new Radio
12611  * @param {Object} config The config object
12612  */
12613
12614 Roo.bootstrap.Radio = function(config){
12615     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12616    
12617 };
12618
12619 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12620     
12621     inputType: 'radio',
12622     inputValue: '',
12623     valueOff: '',
12624     
12625     getAutoCreate : function()
12626     {
12627         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12628         
12629         var id = Roo.id();
12630         
12631         var cfg = {};
12632         
12633         cfg.cls = 'form-group' //input-group
12634         
12635         var input =  {
12636             tag: 'input',
12637             id : id,
12638             type : this.inputType,
12639             value : (!this.checked) ? this.valueOff : this.inputValue,
12640             cls : 'form-box',
12641             placeholder : this.placeholder || ''
12642             
12643         };
12644         
12645         if (this.disabled) {
12646             input.disabled=true;
12647         }
12648         
12649         if(this.checked){
12650             input.checked = this.checked;
12651         }
12652         
12653         if (this.name) {
12654             input.name = this.name;
12655         }
12656         
12657         if (this.size) {
12658             input.cls += ' input-' + this.size;
12659         }
12660         
12661         var settings=this;
12662         ['xs','sm','md','lg'].map(function(size){
12663             if (settings[size]) {
12664                 cfg.cls += ' col-' + size + '-' + settings[size];
12665             }
12666         });
12667         
12668         var inputblock = input;
12669         
12670         if (this.before || this.after) {
12671             
12672             inputblock = {
12673                 cls : 'input-group',
12674                 cn :  [] 
12675             };
12676             if (this.before) {
12677                 inputblock.cn.push({
12678                     tag :'span',
12679                     cls : 'input-group-addon',
12680                     html : this.before
12681                 });
12682             }
12683             inputblock.cn.push(input);
12684             if (this.after) {
12685                 inputblock.cn.push({
12686                     tag :'span',
12687                     cls : 'input-group-addon',
12688                     html : this.after
12689                 });
12690             }
12691             
12692         };
12693         
12694         if (align ==='left' && this.fieldLabel.length) {
12695                 Roo.log("left and has label");
12696                 cfg.cn = [
12697                     
12698                     {
12699                         tag: 'label',
12700                         'for' :  id,
12701                         cls : 'control-label col-md-' + this.labelWidth,
12702                         html : this.fieldLabel
12703                         
12704                     },
12705                     {
12706                         cls : "col-md-" + (12 - this.labelWidth), 
12707                         cn: [
12708                             inputblock
12709                         ]
12710                     }
12711                     
12712                 ];
12713         } else if ( this.fieldLabel.length) {
12714                 Roo.log(" label");
12715                  cfg.cn = [
12716                    
12717                     {
12718                         tag: 'label',
12719                         'for': id,
12720                         cls: 'control-label box-input-label',
12721                         //cls : 'input-group-addon',
12722                         html : this.fieldLabel
12723                         
12724                     },
12725                     
12726                     inputblock
12727                     
12728                 ];
12729
12730         } else {
12731             
12732                    Roo.log(" no label && no align");
12733                 cfg.cn = [
12734                     
12735                         inputblock
12736                     
12737                 ];
12738                 
12739                 
12740         };
12741         
12742         if(this.boxLabel){
12743             cfg.cn.push({
12744                 tag: 'label',
12745                 'for': id,
12746                 cls: 'box-label',
12747                 html: this.boxLabel
12748             })
12749         }
12750         
12751         return cfg;
12752         
12753     },
12754    
12755     onClick : function()
12756     {   
12757         this.setChecked(true);
12758     },
12759     
12760     setChecked : function(state,suppressEvent)
12761     {
12762         if(state){
12763             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12764                 v.dom.checked = false;
12765             });
12766         }
12767         
12768         this.checked = state;
12769         this.inputEl().dom.checked = state;
12770         
12771         if(suppressEvent !== true){
12772             this.fireEvent('check', this, state);
12773         }
12774         
12775         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12776         
12777     },
12778     
12779     getGroupValue : function()
12780     {
12781         var value = ''
12782         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12783             if(v.dom.checked == true){
12784                 value = v.dom.value;
12785             }
12786         });
12787         
12788         return value;
12789     },
12790     
12791     /**
12792      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12793      * @return {Mixed} value The field value
12794      */
12795     getValue : function(){
12796         return this.getGroupValue();
12797     }
12798     
12799 });
12800
12801  
12802 //<script type="text/javascript">
12803
12804 /*
12805  * Based  Ext JS Library 1.1.1
12806  * Copyright(c) 2006-2007, Ext JS, LLC.
12807  * LGPL
12808  *
12809  */
12810  
12811 /**
12812  * @class Roo.HtmlEditorCore
12813  * @extends Roo.Component
12814  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12815  *
12816  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12817  */
12818
12819 Roo.HtmlEditorCore = function(config){
12820     
12821     
12822     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12823     this.addEvents({
12824         /**
12825          * @event initialize
12826          * Fires when the editor is fully initialized (including the iframe)
12827          * @param {Roo.HtmlEditorCore} this
12828          */
12829         initialize: true,
12830         /**
12831          * @event activate
12832          * Fires when the editor is first receives the focus. Any insertion must wait
12833          * until after this event.
12834          * @param {Roo.HtmlEditorCore} this
12835          */
12836         activate: true,
12837          /**
12838          * @event beforesync
12839          * Fires before the textarea is updated with content from the editor iframe. Return false
12840          * to cancel the sync.
12841          * @param {Roo.HtmlEditorCore} this
12842          * @param {String} html
12843          */
12844         beforesync: true,
12845          /**
12846          * @event beforepush
12847          * Fires before the iframe editor is updated with content from the textarea. Return false
12848          * to cancel the push.
12849          * @param {Roo.HtmlEditorCore} this
12850          * @param {String} html
12851          */
12852         beforepush: true,
12853          /**
12854          * @event sync
12855          * Fires when the textarea is updated with content from the editor iframe.
12856          * @param {Roo.HtmlEditorCore} this
12857          * @param {String} html
12858          */
12859         sync: true,
12860          /**
12861          * @event push
12862          * Fires when the iframe editor is updated with content from the textarea.
12863          * @param {Roo.HtmlEditorCore} this
12864          * @param {String} html
12865          */
12866         push: true,
12867         
12868         /**
12869          * @event editorevent
12870          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12871          * @param {Roo.HtmlEditorCore} this
12872          */
12873         editorevent: true
12874     });
12875      
12876 };
12877
12878
12879 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12880
12881
12882      /**
12883      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12884      */
12885     
12886     owner : false,
12887     
12888      /**
12889      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12890      *                        Roo.resizable.
12891      */
12892     resizable : false,
12893      /**
12894      * @cfg {Number} height (in pixels)
12895      */   
12896     height: 300,
12897    /**
12898      * @cfg {Number} width (in pixels)
12899      */   
12900     width: 500,
12901     
12902     /**
12903      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12904      * 
12905      */
12906     stylesheets: false,
12907     
12908     // id of frame..
12909     frameId: false,
12910     
12911     // private properties
12912     validationEvent : false,
12913     deferHeight: true,
12914     initialized : false,
12915     activated : false,
12916     sourceEditMode : false,
12917     onFocus : Roo.emptyFn,
12918     iframePad:3,
12919     hideMode:'offsets',
12920     
12921      
12922     
12923
12924     /**
12925      * Protected method that will not generally be called directly. It
12926      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12927      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12928      */
12929     getDocMarkup : function(){
12930         // body styles..
12931         var st = '';
12932         Roo.log(this.stylesheets);
12933         
12934         // inherit styels from page...?? 
12935         if (this.stylesheets === false) {
12936             
12937             Roo.get(document.head).select('style').each(function(node) {
12938                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12939             });
12940             
12941             Roo.get(document.head).select('link').each(function(node) { 
12942                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12943             });
12944             
12945         } else if (!this.stylesheets.length) {
12946                 // simple..
12947                 st = '<style type="text/css">' +
12948                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12949                    '</style>';
12950         } else {
12951             Roo.each(this.stylesheets, function(s) {
12952                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12953             });
12954             
12955         }
12956         
12957         st +=  '<style type="text/css">' +
12958             'IMG { cursor: pointer } ' +
12959         '</style>';
12960
12961         
12962         return '<html><head>' + st  +
12963             //<style type="text/css">' +
12964             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12965             //'</style>' +
12966             ' </head><body class="roo-htmleditor-body"></body></html>';
12967     },
12968
12969     // private
12970     onRender : function(ct, position)
12971     {
12972         var _t = this;
12973         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12974         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12975         
12976         
12977         this.el.dom.style.border = '0 none';
12978         this.el.dom.setAttribute('tabIndex', -1);
12979         this.el.addClass('x-hidden hide');
12980         
12981         
12982         
12983         if(Roo.isIE){ // fix IE 1px bogus margin
12984             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12985         }
12986        
12987         
12988         this.frameId = Roo.id();
12989         
12990          
12991         
12992         var iframe = this.owner.wrap.createChild({
12993             tag: 'iframe',
12994             cls: 'form-control', // bootstrap..
12995             id: this.frameId,
12996             name: this.frameId,
12997             frameBorder : 'no',
12998             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
12999         }, this.el
13000         );
13001         
13002         
13003         this.iframe = iframe.dom;
13004
13005          this.assignDocWin();
13006         
13007         this.doc.designMode = 'on';
13008        
13009         this.doc.open();
13010         this.doc.write(this.getDocMarkup());
13011         this.doc.close();
13012
13013         
13014         var task = { // must defer to wait for browser to be ready
13015             run : function(){
13016                 //console.log("run task?" + this.doc.readyState);
13017                 this.assignDocWin();
13018                 if(this.doc.body || this.doc.readyState == 'complete'){
13019                     try {
13020                         this.doc.designMode="on";
13021                     } catch (e) {
13022                         return;
13023                     }
13024                     Roo.TaskMgr.stop(task);
13025                     this.initEditor.defer(10, this);
13026                 }
13027             },
13028             interval : 10,
13029             duration: 10000,
13030             scope: this
13031         };
13032         Roo.TaskMgr.start(task);
13033
13034         
13035          
13036     },
13037
13038     // private
13039     onResize : function(w, h)
13040     {
13041          Roo.log('resize: ' +w + ',' + h );
13042         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13043         if(!this.iframe){
13044             return;
13045         }
13046         if(typeof w == 'number'){
13047             
13048             this.iframe.style.width = w + 'px';
13049         }
13050         if(typeof h == 'number'){
13051             
13052             this.iframe.style.height = h + 'px';
13053             if(this.doc){
13054                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13055             }
13056         }
13057         
13058     },
13059
13060     /**
13061      * Toggles the editor between standard and source edit mode.
13062      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13063      */
13064     toggleSourceEdit : function(sourceEditMode){
13065         
13066         this.sourceEditMode = sourceEditMode === true;
13067         
13068         if(this.sourceEditMode){
13069  
13070             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13071             
13072         }else{
13073             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13074             //this.iframe.className = '';
13075             this.deferFocus();
13076         }
13077         //this.setSize(this.owner.wrap.getSize());
13078         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13079     },
13080
13081     
13082   
13083
13084     /**
13085      * Protected method that will not generally be called directly. If you need/want
13086      * custom HTML cleanup, this is the method you should override.
13087      * @param {String} html The HTML to be cleaned
13088      * return {String} The cleaned HTML
13089      */
13090     cleanHtml : function(html){
13091         html = String(html);
13092         if(html.length > 5){
13093             if(Roo.isSafari){ // strip safari nonsense
13094                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13095             }
13096         }
13097         if(html == '&nbsp;'){
13098             html = '';
13099         }
13100         return html;
13101     },
13102
13103     /**
13104      * HTML Editor -> Textarea
13105      * Protected method that will not generally be called directly. Syncs the contents
13106      * of the editor iframe with the textarea.
13107      */
13108     syncValue : function(){
13109         if(this.initialized){
13110             var bd = (this.doc.body || this.doc.documentElement);
13111             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13112             var html = bd.innerHTML;
13113             if(Roo.isSafari){
13114                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13115                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13116                 if(m && m[1]){
13117                     html = '<div style="'+m[0]+'">' + html + '</div>';
13118                 }
13119             }
13120             html = this.cleanHtml(html);
13121             // fix up the special chars.. normaly like back quotes in word...
13122             // however we do not want to do this with chinese..
13123             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13124                 var cc = b.charCodeAt();
13125                 if (
13126                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13127                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13128                     (cc >= 0xf900 && cc < 0xfb00 )
13129                 ) {
13130                         return b;
13131                 }
13132                 return "&#"+cc+";" 
13133             });
13134             if(this.owner.fireEvent('beforesync', this, html) !== false){
13135                 this.el.dom.value = html;
13136                 this.owner.fireEvent('sync', this, html);
13137             }
13138         }
13139     },
13140
13141     /**
13142      * Protected method that will not generally be called directly. Pushes the value of the textarea
13143      * into the iframe editor.
13144      */
13145     pushValue : function(){
13146         if(this.initialized){
13147             var v = this.el.dom.value.trim();
13148             
13149 //            if(v.length < 1){
13150 //                v = '&#160;';
13151 //            }
13152             
13153             if(this.owner.fireEvent('beforepush', this, v) !== false){
13154                 var d = (this.doc.body || this.doc.documentElement);
13155                 d.innerHTML = v;
13156                 this.cleanUpPaste();
13157                 this.el.dom.value = d.innerHTML;
13158                 this.owner.fireEvent('push', this, v);
13159             }
13160         }
13161     },
13162
13163     // private
13164     deferFocus : function(){
13165         this.focus.defer(10, this);
13166     },
13167
13168     // doc'ed in Field
13169     focus : function(){
13170         if(this.win && !this.sourceEditMode){
13171             this.win.focus();
13172         }else{
13173             this.el.focus();
13174         }
13175     },
13176     
13177     assignDocWin: function()
13178     {
13179         var iframe = this.iframe;
13180         
13181          if(Roo.isIE){
13182             this.doc = iframe.contentWindow.document;
13183             this.win = iframe.contentWindow;
13184         } else {
13185             if (!Roo.get(this.frameId)) {
13186                 return;
13187             }
13188             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13189             this.win = Roo.get(this.frameId).dom.contentWindow;
13190         }
13191     },
13192     
13193     // private
13194     initEditor : function(){
13195         //console.log("INIT EDITOR");
13196         this.assignDocWin();
13197         
13198         
13199         
13200         this.doc.designMode="on";
13201         this.doc.open();
13202         this.doc.write(this.getDocMarkup());
13203         this.doc.close();
13204         
13205         var dbody = (this.doc.body || this.doc.documentElement);
13206         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13207         // this copies styles from the containing element into thsi one..
13208         // not sure why we need all of this..
13209         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13210         ss['background-attachment'] = 'fixed'; // w3c
13211         dbody.bgProperties = 'fixed'; // ie
13212         Roo.DomHelper.applyStyles(dbody, ss);
13213         Roo.EventManager.on(this.doc, {
13214             //'mousedown': this.onEditorEvent,
13215             'mouseup': this.onEditorEvent,
13216             'dblclick': this.onEditorEvent,
13217             'click': this.onEditorEvent,
13218             'keyup': this.onEditorEvent,
13219             buffer:100,
13220             scope: this
13221         });
13222         if(Roo.isGecko){
13223             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13224         }
13225         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13226             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13227         }
13228         this.initialized = true;
13229
13230         this.owner.fireEvent('initialize', this);
13231         this.pushValue();
13232     },
13233
13234     // private
13235     onDestroy : function(){
13236         
13237         
13238         
13239         if(this.rendered){
13240             
13241             //for (var i =0; i < this.toolbars.length;i++) {
13242             //    // fixme - ask toolbars for heights?
13243             //    this.toolbars[i].onDestroy();
13244            // }
13245             
13246             //this.wrap.dom.innerHTML = '';
13247             //this.wrap.remove();
13248         }
13249     },
13250
13251     // private
13252     onFirstFocus : function(){
13253         
13254         this.assignDocWin();
13255         
13256         
13257         this.activated = true;
13258          
13259     
13260         if(Roo.isGecko){ // prevent silly gecko errors
13261             this.win.focus();
13262             var s = this.win.getSelection();
13263             if(!s.focusNode || s.focusNode.nodeType != 3){
13264                 var r = s.getRangeAt(0);
13265                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13266                 r.collapse(true);
13267                 this.deferFocus();
13268             }
13269             try{
13270                 this.execCmd('useCSS', true);
13271                 this.execCmd('styleWithCSS', false);
13272             }catch(e){}
13273         }
13274         this.owner.fireEvent('activate', this);
13275     },
13276
13277     // private
13278     adjustFont: function(btn){
13279         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13280         //if(Roo.isSafari){ // safari
13281         //    adjust *= 2;
13282        // }
13283         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13284         if(Roo.isSafari){ // safari
13285             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13286             v =  (v < 10) ? 10 : v;
13287             v =  (v > 48) ? 48 : v;
13288             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13289             
13290         }
13291         
13292         
13293         v = Math.max(1, v+adjust);
13294         
13295         this.execCmd('FontSize', v  );
13296     },
13297
13298     onEditorEvent : function(e){
13299         this.owner.fireEvent('editorevent', this, e);
13300       //  this.updateToolbar();
13301         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13302     },
13303
13304     insertTag : function(tg)
13305     {
13306         // could be a bit smarter... -> wrap the current selected tRoo..
13307         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13308             
13309             range = this.createRange(this.getSelection());
13310             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13311             wrappingNode.appendChild(range.extractContents());
13312             range.insertNode(wrappingNode);
13313
13314             return;
13315             
13316             
13317             
13318         }
13319         this.execCmd("formatblock",   tg);
13320         
13321     },
13322     
13323     insertText : function(txt)
13324     {
13325         
13326         
13327         var range = this.createRange();
13328         range.deleteContents();
13329                //alert(Sender.getAttribute('label'));
13330                
13331         range.insertNode(this.doc.createTextNode(txt));
13332     } ,
13333     
13334      
13335
13336     /**
13337      * Executes a Midas editor command on the editor document and performs necessary focus and
13338      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13339      * @param {String} cmd The Midas command
13340      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13341      */
13342     relayCmd : function(cmd, value){
13343         this.win.focus();
13344         this.execCmd(cmd, value);
13345         this.owner.fireEvent('editorevent', this);
13346         //this.updateToolbar();
13347         this.owner.deferFocus();
13348     },
13349
13350     /**
13351      * Executes a Midas editor command directly on the editor document.
13352      * For visual commands, you should use {@link #relayCmd} instead.
13353      * <b>This should only be called after the editor is initialized.</b>
13354      * @param {String} cmd The Midas command
13355      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13356      */
13357     execCmd : function(cmd, value){
13358         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13359         this.syncValue();
13360     },
13361  
13362  
13363    
13364     /**
13365      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13366      * to insert tRoo.
13367      * @param {String} text | dom node.. 
13368      */
13369     insertAtCursor : function(text)
13370     {
13371         
13372         
13373         
13374         if(!this.activated){
13375             return;
13376         }
13377         /*
13378         if(Roo.isIE){
13379             this.win.focus();
13380             var r = this.doc.selection.createRange();
13381             if(r){
13382                 r.collapse(true);
13383                 r.pasteHTML(text);
13384                 this.syncValue();
13385                 this.deferFocus();
13386             
13387             }
13388             return;
13389         }
13390         */
13391         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13392             this.win.focus();
13393             
13394             
13395             // from jquery ui (MIT licenced)
13396             var range, node;
13397             var win = this.win;
13398             
13399             if (win.getSelection && win.getSelection().getRangeAt) {
13400                 range = win.getSelection().getRangeAt(0);
13401                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13402                 range.insertNode(node);
13403             } else if (win.document.selection && win.document.selection.createRange) {
13404                 // no firefox support
13405                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13406                 win.document.selection.createRange().pasteHTML(txt);
13407             } else {
13408                 // no firefox support
13409                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13410                 this.execCmd('InsertHTML', txt);
13411             } 
13412             
13413             this.syncValue();
13414             
13415             this.deferFocus();
13416         }
13417     },
13418  // private
13419     mozKeyPress : function(e){
13420         if(e.ctrlKey){
13421             var c = e.getCharCode(), cmd;
13422           
13423             if(c > 0){
13424                 c = String.fromCharCode(c).toLowerCase();
13425                 switch(c){
13426                     case 'b':
13427                         cmd = 'bold';
13428                         break;
13429                     case 'i':
13430                         cmd = 'italic';
13431                         break;
13432                     
13433                     case 'u':
13434                         cmd = 'underline';
13435                         break;
13436                     
13437                     case 'v':
13438                         this.cleanUpPaste.defer(100, this);
13439                         return;
13440                         
13441                 }
13442                 if(cmd){
13443                     this.win.focus();
13444                     this.execCmd(cmd);
13445                     this.deferFocus();
13446                     e.preventDefault();
13447                 }
13448                 
13449             }
13450         }
13451     },
13452
13453     // private
13454     fixKeys : function(){ // load time branching for fastest keydown performance
13455         if(Roo.isIE){
13456             return function(e){
13457                 var k = e.getKey(), r;
13458                 if(k == e.TAB){
13459                     e.stopEvent();
13460                     r = this.doc.selection.createRange();
13461                     if(r){
13462                         r.collapse(true);
13463                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13464                         this.deferFocus();
13465                     }
13466                     return;
13467                 }
13468                 
13469                 if(k == e.ENTER){
13470                     r = this.doc.selection.createRange();
13471                     if(r){
13472                         var target = r.parentElement();
13473                         if(!target || target.tagName.toLowerCase() != 'li'){
13474                             e.stopEvent();
13475                             r.pasteHTML('<br />');
13476                             r.collapse(false);
13477                             r.select();
13478                         }
13479                     }
13480                 }
13481                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13482                     this.cleanUpPaste.defer(100, this);
13483                     return;
13484                 }
13485                 
13486                 
13487             };
13488         }else if(Roo.isOpera){
13489             return function(e){
13490                 var k = e.getKey();
13491                 if(k == e.TAB){
13492                     e.stopEvent();
13493                     this.win.focus();
13494                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13495                     this.deferFocus();
13496                 }
13497                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13498                     this.cleanUpPaste.defer(100, this);
13499                     return;
13500                 }
13501                 
13502             };
13503         }else if(Roo.isSafari){
13504             return function(e){
13505                 var k = e.getKey();
13506                 
13507                 if(k == e.TAB){
13508                     e.stopEvent();
13509                     this.execCmd('InsertText','\t');
13510                     this.deferFocus();
13511                     return;
13512                 }
13513                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13514                     this.cleanUpPaste.defer(100, this);
13515                     return;
13516                 }
13517                 
13518              };
13519         }
13520     }(),
13521     
13522     getAllAncestors: function()
13523     {
13524         var p = this.getSelectedNode();
13525         var a = [];
13526         if (!p) {
13527             a.push(p); // push blank onto stack..
13528             p = this.getParentElement();
13529         }
13530         
13531         
13532         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13533             a.push(p);
13534             p = p.parentNode;
13535         }
13536         a.push(this.doc.body);
13537         return a;
13538     },
13539     lastSel : false,
13540     lastSelNode : false,
13541     
13542     
13543     getSelection : function() 
13544     {
13545         this.assignDocWin();
13546         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13547     },
13548     
13549     getSelectedNode: function() 
13550     {
13551         // this may only work on Gecko!!!
13552         
13553         // should we cache this!!!!
13554         
13555         
13556         
13557          
13558         var range = this.createRange(this.getSelection()).cloneRange();
13559         
13560         if (Roo.isIE) {
13561             var parent = range.parentElement();
13562             while (true) {
13563                 var testRange = range.duplicate();
13564                 testRange.moveToElementText(parent);
13565                 if (testRange.inRange(range)) {
13566                     break;
13567                 }
13568                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13569                     break;
13570                 }
13571                 parent = parent.parentElement;
13572             }
13573             return parent;
13574         }
13575         
13576         // is ancestor a text element.
13577         var ac =  range.commonAncestorContainer;
13578         if (ac.nodeType == 3) {
13579             ac = ac.parentNode;
13580         }
13581         
13582         var ar = ac.childNodes;
13583          
13584         var nodes = [];
13585         var other_nodes = [];
13586         var has_other_nodes = false;
13587         for (var i=0;i<ar.length;i++) {
13588             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13589                 continue;
13590             }
13591             // fullly contained node.
13592             
13593             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13594                 nodes.push(ar[i]);
13595                 continue;
13596             }
13597             
13598             // probably selected..
13599             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13600                 other_nodes.push(ar[i]);
13601                 continue;
13602             }
13603             // outer..
13604             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13605                 continue;
13606             }
13607             
13608             
13609             has_other_nodes = true;
13610         }
13611         if (!nodes.length && other_nodes.length) {
13612             nodes= other_nodes;
13613         }
13614         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13615             return false;
13616         }
13617         
13618         return nodes[0];
13619     },
13620     createRange: function(sel)
13621     {
13622         // this has strange effects when using with 
13623         // top toolbar - not sure if it's a great idea.
13624         //this.editor.contentWindow.focus();
13625         if (typeof sel != "undefined") {
13626             try {
13627                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13628             } catch(e) {
13629                 return this.doc.createRange();
13630             }
13631         } else {
13632             return this.doc.createRange();
13633         }
13634     },
13635     getParentElement: function()
13636     {
13637         
13638         this.assignDocWin();
13639         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13640         
13641         var range = this.createRange(sel);
13642          
13643         try {
13644             var p = range.commonAncestorContainer;
13645             while (p.nodeType == 3) { // text node
13646                 p = p.parentNode;
13647             }
13648             return p;
13649         } catch (e) {
13650             return null;
13651         }
13652     
13653     },
13654     /***
13655      *
13656      * Range intersection.. the hard stuff...
13657      *  '-1' = before
13658      *  '0' = hits..
13659      *  '1' = after.
13660      *         [ -- selected range --- ]
13661      *   [fail]                        [fail]
13662      *
13663      *    basically..
13664      *      if end is before start or  hits it. fail.
13665      *      if start is after end or hits it fail.
13666      *
13667      *   if either hits (but other is outside. - then it's not 
13668      *   
13669      *    
13670      **/
13671     
13672     
13673     // @see http://www.thismuchiknow.co.uk/?p=64.
13674     rangeIntersectsNode : function(range, node)
13675     {
13676         var nodeRange = node.ownerDocument.createRange();
13677         try {
13678             nodeRange.selectNode(node);
13679         } catch (e) {
13680             nodeRange.selectNodeContents(node);
13681         }
13682     
13683         var rangeStartRange = range.cloneRange();
13684         rangeStartRange.collapse(true);
13685     
13686         var rangeEndRange = range.cloneRange();
13687         rangeEndRange.collapse(false);
13688     
13689         var nodeStartRange = nodeRange.cloneRange();
13690         nodeStartRange.collapse(true);
13691     
13692         var nodeEndRange = nodeRange.cloneRange();
13693         nodeEndRange.collapse(false);
13694     
13695         return rangeStartRange.compareBoundaryPoints(
13696                  Range.START_TO_START, nodeEndRange) == -1 &&
13697                rangeEndRange.compareBoundaryPoints(
13698                  Range.START_TO_START, nodeStartRange) == 1;
13699         
13700          
13701     },
13702     rangeCompareNode : function(range, node)
13703     {
13704         var nodeRange = node.ownerDocument.createRange();
13705         try {
13706             nodeRange.selectNode(node);
13707         } catch (e) {
13708             nodeRange.selectNodeContents(node);
13709         }
13710         
13711         
13712         range.collapse(true);
13713     
13714         nodeRange.collapse(true);
13715      
13716         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13717         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13718          
13719         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13720         
13721         var nodeIsBefore   =  ss == 1;
13722         var nodeIsAfter    = ee == -1;
13723         
13724         if (nodeIsBefore && nodeIsAfter)
13725             return 0; // outer
13726         if (!nodeIsBefore && nodeIsAfter)
13727             return 1; //right trailed.
13728         
13729         if (nodeIsBefore && !nodeIsAfter)
13730             return 2;  // left trailed.
13731         // fully contined.
13732         return 3;
13733     },
13734
13735     // private? - in a new class?
13736     cleanUpPaste :  function()
13737     {
13738         // cleans up the whole document..
13739          Roo.log('cleanuppaste');
13740         this.cleanUpChildren(this.doc.body);
13741         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13742         if (clean != this.doc.body.innerHTML) {
13743             this.doc.body.innerHTML = clean;
13744         }
13745         
13746     },
13747     
13748     cleanWordChars : function(input) {// change the chars to hex code
13749         var he = Roo.HtmlEditorCore;
13750         
13751         var output = input;
13752         Roo.each(he.swapCodes, function(sw) { 
13753             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13754             
13755             output = output.replace(swapper, sw[1]);
13756         });
13757         
13758         return output;
13759     },
13760     
13761     
13762     cleanUpChildren : function (n)
13763     {
13764         if (!n.childNodes.length) {
13765             return;
13766         }
13767         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13768            this.cleanUpChild(n.childNodes[i]);
13769         }
13770     },
13771     
13772     
13773         
13774     
13775     cleanUpChild : function (node)
13776     {
13777         var ed = this;
13778         //console.log(node);
13779         if (node.nodeName == "#text") {
13780             // clean up silly Windows -- stuff?
13781             return; 
13782         }
13783         if (node.nodeName == "#comment") {
13784             node.parentNode.removeChild(node);
13785             // clean up silly Windows -- stuff?
13786             return; 
13787         }
13788         
13789         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
13790             // remove node.
13791             node.parentNode.removeChild(node);
13792             return;
13793             
13794         }
13795         
13796         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13797         
13798         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13799         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13800         
13801         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13802         //    remove_keep_children = true;
13803         //}
13804         
13805         if (remove_keep_children) {
13806             this.cleanUpChildren(node);
13807             // inserts everything just before this node...
13808             while (node.childNodes.length) {
13809                 var cn = node.childNodes[0];
13810                 node.removeChild(cn);
13811                 node.parentNode.insertBefore(cn, node);
13812             }
13813             node.parentNode.removeChild(node);
13814             return;
13815         }
13816         
13817         if (!node.attributes || !node.attributes.length) {
13818             this.cleanUpChildren(node);
13819             return;
13820         }
13821         
13822         function cleanAttr(n,v)
13823         {
13824             
13825             if (v.match(/^\./) || v.match(/^\//)) {
13826                 return;
13827             }
13828             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13829                 return;
13830             }
13831             if (v.match(/^#/)) {
13832                 return;
13833             }
13834 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13835             node.removeAttribute(n);
13836             
13837         }
13838         
13839         function cleanStyle(n,v)
13840         {
13841             if (v.match(/expression/)) { //XSS?? should we even bother..
13842                 node.removeAttribute(n);
13843                 return;
13844             }
13845             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13846             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13847             
13848             
13849             var parts = v.split(/;/);
13850             var clean = [];
13851             
13852             Roo.each(parts, function(p) {
13853                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13854                 if (!p.length) {
13855                     return true;
13856                 }
13857                 var l = p.split(':').shift().replace(/\s+/g,'');
13858                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13859                 
13860                 
13861                 if ( cblack.indexOf(l) > -1) {
13862 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13863                     //node.removeAttribute(n);
13864                     return true;
13865                 }
13866                 //Roo.log()
13867                 // only allow 'c whitelisted system attributes'
13868                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13869 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13870                     //node.removeAttribute(n);
13871                     return true;
13872                 }
13873                 
13874                 
13875                  
13876                 
13877                 clean.push(p);
13878                 return true;
13879             });
13880             if (clean.length) { 
13881                 node.setAttribute(n, clean.join(';'));
13882             } else {
13883                 node.removeAttribute(n);
13884             }
13885             
13886         }
13887         
13888         
13889         for (var i = node.attributes.length-1; i > -1 ; i--) {
13890             var a = node.attributes[i];
13891             //console.log(a);
13892             
13893             if (a.name.toLowerCase().substr(0,2)=='on')  {
13894                 node.removeAttribute(a.name);
13895                 continue;
13896             }
13897             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13898                 node.removeAttribute(a.name);
13899                 continue;
13900             }
13901             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13902                 cleanAttr(a.name,a.value); // fixme..
13903                 continue;
13904             }
13905             if (a.name == 'style') {
13906                 cleanStyle(a.name,a.value);
13907                 continue;
13908             }
13909             /// clean up MS crap..
13910             // tecnically this should be a list of valid class'es..
13911             
13912             
13913             if (a.name == 'class') {
13914                 if (a.value.match(/^Mso/)) {
13915                     node.className = '';
13916                 }
13917                 
13918                 if (a.value.match(/body/)) {
13919                     node.className = '';
13920                 }
13921                 continue;
13922             }
13923             
13924             // style cleanup!?
13925             // class cleanup?
13926             
13927         }
13928         
13929         
13930         this.cleanUpChildren(node);
13931         
13932         
13933     }
13934     
13935     
13936     // hide stuff that is not compatible
13937     /**
13938      * @event blur
13939      * @hide
13940      */
13941     /**
13942      * @event change
13943      * @hide
13944      */
13945     /**
13946      * @event focus
13947      * @hide
13948      */
13949     /**
13950      * @event specialkey
13951      * @hide
13952      */
13953     /**
13954      * @cfg {String} fieldClass @hide
13955      */
13956     /**
13957      * @cfg {String} focusClass @hide
13958      */
13959     /**
13960      * @cfg {String} autoCreate @hide
13961      */
13962     /**
13963      * @cfg {String} inputType @hide
13964      */
13965     /**
13966      * @cfg {String} invalidClass @hide
13967      */
13968     /**
13969      * @cfg {String} invalidText @hide
13970      */
13971     /**
13972      * @cfg {String} msgFx @hide
13973      */
13974     /**
13975      * @cfg {String} validateOnBlur @hide
13976      */
13977 });
13978
13979 Roo.HtmlEditorCore.white = [
13980         'area', 'br', 'img', 'input', 'hr', 'wbr',
13981         
13982        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
13983        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
13984        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
13985        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
13986        'table',   'ul',         'xmp', 
13987        
13988        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
13989       'thead',   'tr', 
13990      
13991       'dir', 'menu', 'ol', 'ul', 'dl',
13992        
13993       'embed',  'object'
13994 ];
13995
13996
13997 Roo.HtmlEditorCore.black = [
13998     //    'embed',  'object', // enable - backend responsiblity to clean thiese
13999         'applet', // 
14000         'base',   'basefont', 'bgsound', 'blink',  'body', 
14001         'frame',  'frameset', 'head',    'html',   'ilayer', 
14002         'iframe', 'layer',  'link',     'meta',    'object',   
14003         'script', 'style' ,'title',  'xml' // clean later..
14004 ];
14005 Roo.HtmlEditorCore.clean = [
14006     'script', 'style', 'title', 'xml'
14007 ];
14008 Roo.HtmlEditorCore.remove = [
14009     'font'
14010 ];
14011 // attributes..
14012
14013 Roo.HtmlEditorCore.ablack = [
14014     'on'
14015 ];
14016     
14017 Roo.HtmlEditorCore.aclean = [ 
14018     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14019 ];
14020
14021 // protocols..
14022 Roo.HtmlEditorCore.pwhite= [
14023         'http',  'https',  'mailto'
14024 ];
14025
14026 // white listed style attributes.
14027 Roo.HtmlEditorCore.cwhite= [
14028       //  'text-align', /// default is to allow most things..
14029       
14030          
14031 //        'font-size'//??
14032 ];
14033
14034 // black listed style attributes.
14035 Roo.HtmlEditorCore.cblack= [
14036       //  'font-size' -- this can be set by the project 
14037 ];
14038
14039
14040 Roo.HtmlEditorCore.swapCodes   =[ 
14041     [    8211, "--" ], 
14042     [    8212, "--" ], 
14043     [    8216,  "'" ],  
14044     [    8217, "'" ],  
14045     [    8220, '"' ],  
14046     [    8221, '"' ],  
14047     [    8226, "*" ],  
14048     [    8230, "..." ]
14049 ]; 
14050
14051     /*
14052  * - LGPL
14053  *
14054  * HtmlEditor
14055  * 
14056  */
14057
14058 /**
14059  * @class Roo.bootstrap.HtmlEditor
14060  * @extends Roo.bootstrap.TextArea
14061  * Bootstrap HtmlEditor class
14062
14063  * @constructor
14064  * Create a new HtmlEditor
14065  * @param {Object} config The config object
14066  */
14067
14068 Roo.bootstrap.HtmlEditor = function(config){
14069     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14070     if (!this.toolbars) {
14071         this.toolbars = [];
14072     }
14073     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14074     this.addEvents({
14075             /**
14076              * @event initialize
14077              * Fires when the editor is fully initialized (including the iframe)
14078              * @param {HtmlEditor} this
14079              */
14080             initialize: true,
14081             /**
14082              * @event activate
14083              * Fires when the editor is first receives the focus. Any insertion must wait
14084              * until after this event.
14085              * @param {HtmlEditor} this
14086              */
14087             activate: true,
14088              /**
14089              * @event beforesync
14090              * Fires before the textarea is updated with content from the editor iframe. Return false
14091              * to cancel the sync.
14092              * @param {HtmlEditor} this
14093              * @param {String} html
14094              */
14095             beforesync: true,
14096              /**
14097              * @event beforepush
14098              * Fires before the iframe editor is updated with content from the textarea. Return false
14099              * to cancel the push.
14100              * @param {HtmlEditor} this
14101              * @param {String} html
14102              */
14103             beforepush: true,
14104              /**
14105              * @event sync
14106              * Fires when the textarea is updated with content from the editor iframe.
14107              * @param {HtmlEditor} this
14108              * @param {String} html
14109              */
14110             sync: true,
14111              /**
14112              * @event push
14113              * Fires when the iframe editor is updated with content from the textarea.
14114              * @param {HtmlEditor} this
14115              * @param {String} html
14116              */
14117             push: true,
14118              /**
14119              * @event editmodechange
14120              * Fires when the editor switches edit modes
14121              * @param {HtmlEditor} this
14122              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14123              */
14124             editmodechange: true,
14125             /**
14126              * @event editorevent
14127              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14128              * @param {HtmlEditor} this
14129              */
14130             editorevent: true,
14131             /**
14132              * @event firstfocus
14133              * Fires when on first focus - needed by toolbars..
14134              * @param {HtmlEditor} this
14135              */
14136             firstfocus: true,
14137             /**
14138              * @event autosave
14139              * Auto save the htmlEditor value as a file into Events
14140              * @param {HtmlEditor} this
14141              */
14142             autosave: true,
14143             /**
14144              * @event savedpreview
14145              * preview the saved version of htmlEditor
14146              * @param {HtmlEditor} this
14147              */
14148             savedpreview: true
14149         });
14150 };
14151
14152
14153 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14154     
14155     
14156       /**
14157      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14158      */
14159     toolbars : false,
14160    
14161      /**
14162      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14163      *                        Roo.resizable.
14164      */
14165     resizable : false,
14166      /**
14167      * @cfg {Number} height (in pixels)
14168      */   
14169     height: 300,
14170    /**
14171      * @cfg {Number} width (in pixels)
14172      */   
14173     width: false,
14174     
14175     /**
14176      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14177      * 
14178      */
14179     stylesheets: false,
14180     
14181     // id of frame..
14182     frameId: false,
14183     
14184     // private properties
14185     validationEvent : false,
14186     deferHeight: true,
14187     initialized : false,
14188     activated : false,
14189     
14190     onFocus : Roo.emptyFn,
14191     iframePad:3,
14192     hideMode:'offsets',
14193     
14194     
14195     tbContainer : false,
14196     
14197     toolbarContainer :function() {
14198         return this.wrap.select('.x-html-editor-tb',true).first();
14199     },
14200
14201     /**
14202      * Protected method that will not generally be called directly. It
14203      * is called when the editor creates its toolbar. Override this method if you need to
14204      * add custom toolbar buttons.
14205      * @param {HtmlEditor} editor
14206      */
14207     createToolbar : function(){
14208         
14209         Roo.log("create toolbars");
14210         
14211         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14212         this.toolbars[0].render(this.toolbarContainer());
14213         
14214         return;
14215         
14216 //        if (!editor.toolbars || !editor.toolbars.length) {
14217 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14218 //        }
14219 //        
14220 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14221 //            editor.toolbars[i] = Roo.factory(
14222 //                    typeof(editor.toolbars[i]) == 'string' ?
14223 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14224 //                Roo.bootstrap.HtmlEditor);
14225 //            editor.toolbars[i].init(editor);
14226 //        }
14227     },
14228
14229      
14230     // private
14231     onRender : function(ct, position)
14232     {
14233        // Roo.log("Call onRender: " + this.xtype);
14234         var _t = this;
14235         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14236       
14237         this.wrap = this.inputEl().wrap({
14238             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14239         });
14240         
14241         this.editorcore.onRender(ct, position);
14242          
14243         if (this.resizable) {
14244             this.resizeEl = new Roo.Resizable(this.wrap, {
14245                 pinned : true,
14246                 wrap: true,
14247                 dynamic : true,
14248                 minHeight : this.height,
14249                 height: this.height,
14250                 handles : this.resizable,
14251                 width: this.width,
14252                 listeners : {
14253                     resize : function(r, w, h) {
14254                         _t.onResize(w,h); // -something
14255                     }
14256                 }
14257             });
14258             
14259         }
14260         this.createToolbar(this);
14261        
14262         
14263         if(!this.width && this.resizable){
14264             this.setSize(this.wrap.getSize());
14265         }
14266         if (this.resizeEl) {
14267             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14268             // should trigger onReize..
14269         }
14270         
14271     },
14272
14273     // private
14274     onResize : function(w, h)
14275     {
14276         Roo.log('resize: ' +w + ',' + h );
14277         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14278         var ew = false;
14279         var eh = false;
14280         
14281         if(this.inputEl() ){
14282             if(typeof w == 'number'){
14283                 var aw = w - this.wrap.getFrameWidth('lr');
14284                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14285                 ew = aw;
14286             }
14287             if(typeof h == 'number'){
14288                  var tbh = -11;  // fixme it needs to tool bar size!
14289                 for (var i =0; i < this.toolbars.length;i++) {
14290                     // fixme - ask toolbars for heights?
14291                     tbh += this.toolbars[i].el.getHeight();
14292                     //if (this.toolbars[i].footer) {
14293                     //    tbh += this.toolbars[i].footer.el.getHeight();
14294                     //}
14295                 }
14296               
14297                 
14298                 
14299                 
14300                 
14301                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14302                 ah -= 5; // knock a few pixes off for look..
14303                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14304                 var eh = ah;
14305             }
14306         }
14307         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14308         this.editorcore.onResize(ew,eh);
14309         
14310     },
14311
14312     /**
14313      * Toggles the editor between standard and source edit mode.
14314      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14315      */
14316     toggleSourceEdit : function(sourceEditMode)
14317     {
14318         this.editorcore.toggleSourceEdit(sourceEditMode);
14319         
14320         if(this.editorcore.sourceEditMode){
14321             Roo.log('editor - showing textarea');
14322             
14323 //            Roo.log('in');
14324 //            Roo.log(this.syncValue());
14325             this.syncValue();
14326             this.inputEl().removeClass('hide');
14327             this.inputEl().dom.removeAttribute('tabIndex');
14328             this.inputEl().focus();
14329         }else{
14330             Roo.log('editor - hiding textarea');
14331 //            Roo.log('out')
14332 //            Roo.log(this.pushValue()); 
14333             this.pushValue();
14334             
14335             this.inputEl().addClass('hide');
14336             this.inputEl().dom.setAttribute('tabIndex', -1);
14337             //this.deferFocus();
14338         }
14339          
14340         if(this.resizable){
14341             this.setSize(this.wrap.getSize());
14342         }
14343         
14344         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14345     },
14346  
14347     // private (for BoxComponent)
14348     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14349
14350     // private (for BoxComponent)
14351     getResizeEl : function(){
14352         return this.wrap;
14353     },
14354
14355     // private (for BoxComponent)
14356     getPositionEl : function(){
14357         return this.wrap;
14358     },
14359
14360     // private
14361     initEvents : function(){
14362         this.originalValue = this.getValue();
14363     },
14364
14365 //    /**
14366 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14367 //     * @method
14368 //     */
14369 //    markInvalid : Roo.emptyFn,
14370 //    /**
14371 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14372 //     * @method
14373 //     */
14374 //    clearInvalid : Roo.emptyFn,
14375
14376     setValue : function(v){
14377         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14378         this.editorcore.pushValue();
14379     },
14380
14381      
14382     // private
14383     deferFocus : function(){
14384         this.focus.defer(10, this);
14385     },
14386
14387     // doc'ed in Field
14388     focus : function(){
14389         this.editorcore.focus();
14390         
14391     },
14392       
14393
14394     // private
14395     onDestroy : function(){
14396         
14397         
14398         
14399         if(this.rendered){
14400             
14401             for (var i =0; i < this.toolbars.length;i++) {
14402                 // fixme - ask toolbars for heights?
14403                 this.toolbars[i].onDestroy();
14404             }
14405             
14406             this.wrap.dom.innerHTML = '';
14407             this.wrap.remove();
14408         }
14409     },
14410
14411     // private
14412     onFirstFocus : function(){
14413         //Roo.log("onFirstFocus");
14414         this.editorcore.onFirstFocus();
14415          for (var i =0; i < this.toolbars.length;i++) {
14416             this.toolbars[i].onFirstFocus();
14417         }
14418         
14419     },
14420     
14421     // private
14422     syncValue : function()
14423     {   
14424         this.editorcore.syncValue();
14425     },
14426     
14427     pushValue : function()
14428     {   
14429         this.editorcore.pushValue();
14430     }
14431      
14432     
14433     // hide stuff that is not compatible
14434     /**
14435      * @event blur
14436      * @hide
14437      */
14438     /**
14439      * @event change
14440      * @hide
14441      */
14442     /**
14443      * @event focus
14444      * @hide
14445      */
14446     /**
14447      * @event specialkey
14448      * @hide
14449      */
14450     /**
14451      * @cfg {String} fieldClass @hide
14452      */
14453     /**
14454      * @cfg {String} focusClass @hide
14455      */
14456     /**
14457      * @cfg {String} autoCreate @hide
14458      */
14459     /**
14460      * @cfg {String} inputType @hide
14461      */
14462     /**
14463      * @cfg {String} invalidClass @hide
14464      */
14465     /**
14466      * @cfg {String} invalidText @hide
14467      */
14468     /**
14469      * @cfg {String} msgFx @hide
14470      */
14471     /**
14472      * @cfg {String} validateOnBlur @hide
14473      */
14474 });
14475  
14476     
14477    
14478    
14479    
14480       
14481
14482 /**
14483  * @class Roo.bootstrap.HtmlEditorToolbar1
14484  * Basic Toolbar
14485  * 
14486  * Usage:
14487  *
14488  new Roo.bootstrap.HtmlEditor({
14489     ....
14490     toolbars : [
14491         new Roo.bootstrap.HtmlEditorToolbar1({
14492             disable : { fonts: 1 , format: 1, ..., ... , ...],
14493             btns : [ .... ]
14494         })
14495     }
14496      
14497  * 
14498  * @cfg {Object} disable List of elements to disable..
14499  * @cfg {Array} btns List of additional buttons.
14500  * 
14501  * 
14502  * NEEDS Extra CSS? 
14503  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14504  */
14505  
14506 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14507 {
14508     
14509     Roo.apply(this, config);
14510     
14511     // default disabled, based on 'good practice'..
14512     this.disable = this.disable || {};
14513     Roo.applyIf(this.disable, {
14514         fontSize : true,
14515         colors : true,
14516         specialElements : true
14517     });
14518     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14519     
14520     this.editor = config.editor;
14521     this.editorcore = config.editor.editorcore;
14522     
14523     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14524     
14525     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14526     // dont call parent... till later.
14527 }
14528 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14529     
14530     
14531     bar : true,
14532     
14533     editor : false,
14534     editorcore : false,
14535     
14536     
14537     formats : [
14538         "p" ,  
14539         "h1","h2","h3","h4","h5","h6", 
14540         "pre", "code", 
14541         "abbr", "acronym", "address", "cite", "samp", "var",
14542         'div','span'
14543     ],
14544     
14545     onRender : function(ct, position)
14546     {
14547        // Roo.log("Call onRender: " + this.xtype);
14548         
14549        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14550        Roo.log(this.el);
14551        this.el.dom.style.marginBottom = '0';
14552        var _this = this;
14553        var editorcore = this.editorcore;
14554        var editor= this.editor;
14555        
14556        var children = [];
14557        var btn = function(id,cmd , toggle, handler){
14558        
14559             var  event = toggle ? 'toggle' : 'click';
14560        
14561             var a = {
14562                 size : 'sm',
14563                 xtype: 'Button',
14564                 xns: Roo.bootstrap,
14565                 glyphicon : id,
14566                 cmd : id || cmd,
14567                 enableToggle:toggle !== false,
14568                 //html : 'submit'
14569                 pressed : toggle ? false : null,
14570                 listeners : {}
14571             }
14572             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14573                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14574             }
14575             children.push(a);
14576             return a;
14577        }
14578         
14579         var style = {
14580                 xtype: 'Button',
14581                 size : 'sm',
14582                 xns: Roo.bootstrap,
14583                 glyphicon : 'font',
14584                 //html : 'submit'
14585                 menu : {
14586                     xtype: 'Menu',
14587                     xns: Roo.bootstrap,
14588                     items:  []
14589                 }
14590         };
14591         Roo.each(this.formats, function(f) {
14592             style.menu.items.push({
14593                 xtype :'MenuItem',
14594                 xns: Roo.bootstrap,
14595                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14596                 tagname : f,
14597                 listeners : {
14598                     click : function()
14599                     {
14600                         editorcore.insertTag(this.tagname);
14601                         editor.focus();
14602                     }
14603                 }
14604                 
14605             });
14606         });
14607          children.push(style);   
14608             
14609             
14610         btn('bold',false,true);
14611         btn('italic',false,true);
14612         btn('align-left', 'justifyleft',true);
14613         btn('align-center', 'justifycenter',true);
14614         btn('align-right' , 'justifyright',true);
14615         btn('link', false, false, function(btn) {
14616             //Roo.log("create link?");
14617             var url = prompt(this.createLinkText, this.defaultLinkValue);
14618             if(url && url != 'http:/'+'/'){
14619                 this.editorcore.relayCmd('createlink', url);
14620             }
14621         }),
14622         btn('list','insertunorderedlist',true);
14623         btn('pencil', false,true, function(btn){
14624                 Roo.log(this);
14625                 
14626                 this.toggleSourceEdit(btn.pressed);
14627         });
14628         /*
14629         var cog = {
14630                 xtype: 'Button',
14631                 size : 'sm',
14632                 xns: Roo.bootstrap,
14633                 glyphicon : 'cog',
14634                 //html : 'submit'
14635                 menu : {
14636                     xtype: 'Menu',
14637                     xns: Roo.bootstrap,
14638                     items:  []
14639                 }
14640         };
14641         
14642         cog.menu.items.push({
14643             xtype :'MenuItem',
14644             xns: Roo.bootstrap,
14645             html : Clean styles,
14646             tagname : f,
14647             listeners : {
14648                 click : function()
14649                 {
14650                     editorcore.insertTag(this.tagname);
14651                     editor.focus();
14652                 }
14653             }
14654             
14655         });
14656        */
14657         
14658          
14659        this.xtype = 'Navbar';
14660         
14661         for(var i=0;i< children.length;i++) {
14662             
14663             this.buttons.add(this.addxtypeChild(children[i]));
14664             
14665         }
14666         
14667         editor.on('editorevent', this.updateToolbar, this);
14668     },
14669     onBtnClick : function(id)
14670     {
14671        this.editorcore.relayCmd(id);
14672        this.editorcore.focus();
14673     },
14674     
14675     /**
14676      * Protected method that will not generally be called directly. It triggers
14677      * a toolbar update by reading the markup state of the current selection in the editor.
14678      */
14679     updateToolbar: function(){
14680
14681         if(!this.editorcore.activated){
14682             this.editor.onFirstFocus(); // is this neeed?
14683             return;
14684         }
14685
14686         var btns = this.buttons; 
14687         var doc = this.editorcore.doc;
14688         btns.get('bold').setActive(doc.queryCommandState('bold'));
14689         btns.get('italic').setActive(doc.queryCommandState('italic'));
14690         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14691         
14692         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14693         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14694         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14695         
14696         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14697         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14698          /*
14699         
14700         var ans = this.editorcore.getAllAncestors();
14701         if (this.formatCombo) {
14702             
14703             
14704             var store = this.formatCombo.store;
14705             this.formatCombo.setValue("");
14706             for (var i =0; i < ans.length;i++) {
14707                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14708                     // select it..
14709                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14710                     break;
14711                 }
14712             }
14713         }
14714         
14715         
14716         
14717         // hides menus... - so this cant be on a menu...
14718         Roo.bootstrap.MenuMgr.hideAll();
14719         */
14720         Roo.bootstrap.MenuMgr.hideAll();
14721         //this.editorsyncValue();
14722     },
14723     onFirstFocus: function() {
14724         this.buttons.each(function(item){
14725            item.enable();
14726         });
14727     },
14728     toggleSourceEdit : function(sourceEditMode){
14729         
14730           
14731         if(sourceEditMode){
14732             Roo.log("disabling buttons");
14733            this.buttons.each( function(item){
14734                 if(item.cmd != 'pencil'){
14735                     item.disable();
14736                 }
14737             });
14738           
14739         }else{
14740             Roo.log("enabling buttons");
14741             if(this.editorcore.initialized){
14742                 this.buttons.each( function(item){
14743                     item.enable();
14744                 });
14745             }
14746             
14747         }
14748         Roo.log("calling toggole on editor");
14749         // tell the editor that it's been pressed..
14750         this.editor.toggleSourceEdit(sourceEditMode);
14751        
14752     }
14753 });
14754
14755
14756
14757
14758
14759 /**
14760  * @class Roo.bootstrap.Table.AbstractSelectionModel
14761  * @extends Roo.util.Observable
14762  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14763  * implemented by descendant classes.  This class should not be directly instantiated.
14764  * @constructor
14765  */
14766 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14767     this.locked = false;
14768     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14769 };
14770
14771
14772 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14773     /** @ignore Called by the grid automatically. Do not call directly. */
14774     init : function(grid){
14775         this.grid = grid;
14776         this.initEvents();
14777     },
14778
14779     /**
14780      * Locks the selections.
14781      */
14782     lock : function(){
14783         this.locked = true;
14784     },
14785
14786     /**
14787      * Unlocks the selections.
14788      */
14789     unlock : function(){
14790         this.locked = false;
14791     },
14792
14793     /**
14794      * Returns true if the selections are locked.
14795      * @return {Boolean}
14796      */
14797     isLocked : function(){
14798         return this.locked;
14799     }
14800 });
14801 /**
14802  * @class Roo.bootstrap.Table.ColumnModel
14803  * @extends Roo.util.Observable
14804  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14805  * the columns in the table.
14806  
14807  * @constructor
14808  * @param {Object} config An Array of column config objects. See this class's
14809  * config objects for details.
14810 */
14811 Roo.bootstrap.Table.ColumnModel = function(config){
14812         /**
14813      * The config passed into the constructor
14814      */
14815     this.config = config;
14816     this.lookup = {};
14817
14818     // if no id, create one
14819     // if the column does not have a dataIndex mapping,
14820     // map it to the order it is in the config
14821     for(var i = 0, len = config.length; i < len; i++){
14822         var c = config[i];
14823         if(typeof c.dataIndex == "undefined"){
14824             c.dataIndex = i;
14825         }
14826         if(typeof c.renderer == "string"){
14827             c.renderer = Roo.util.Format[c.renderer];
14828         }
14829         if(typeof c.id == "undefined"){
14830             c.id = Roo.id();
14831         }
14832 //        if(c.editor && c.editor.xtype){
14833 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14834 //        }
14835 //        if(c.editor && c.editor.isFormField){
14836 //            c.editor = new Roo.grid.GridEditor(c.editor);
14837 //        }
14838
14839         this.lookup[c.id] = c;
14840     }
14841
14842     /**
14843      * The width of columns which have no width specified (defaults to 100)
14844      * @type Number
14845      */
14846     this.defaultWidth = 100;
14847
14848     /**
14849      * Default sortable of columns which have no sortable specified (defaults to false)
14850      * @type Boolean
14851      */
14852     this.defaultSortable = false;
14853
14854     this.addEvents({
14855         /**
14856              * @event widthchange
14857              * Fires when the width of a column changes.
14858              * @param {ColumnModel} this
14859              * @param {Number} columnIndex The column index
14860              * @param {Number} newWidth The new width
14861              */
14862             "widthchange": true,
14863         /**
14864              * @event headerchange
14865              * Fires when the text of a header changes.
14866              * @param {ColumnModel} this
14867              * @param {Number} columnIndex The column index
14868              * @param {Number} newText The new header text
14869              */
14870             "headerchange": true,
14871         /**
14872              * @event hiddenchange
14873              * Fires when a column is hidden or "unhidden".
14874              * @param {ColumnModel} this
14875              * @param {Number} columnIndex The column index
14876              * @param {Boolean} hidden true if hidden, false otherwise
14877              */
14878             "hiddenchange": true,
14879             /**
14880          * @event columnmoved
14881          * Fires when a column is moved.
14882          * @param {ColumnModel} this
14883          * @param {Number} oldIndex
14884          * @param {Number} newIndex
14885          */
14886         "columnmoved" : true,
14887         /**
14888          * @event columlockchange
14889          * Fires when a column's locked state is changed
14890          * @param {ColumnModel} this
14891          * @param {Number} colIndex
14892          * @param {Boolean} locked true if locked
14893          */
14894         "columnlockchange" : true
14895     });
14896     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14897 };
14898 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14899     /**
14900      * @cfg {String} header The header text to display in the Grid view.
14901      */
14902     /**
14903      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14904      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14905      * specified, the column's index is used as an index into the Record's data Array.
14906      */
14907     /**
14908      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14909      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14910      */
14911     /**
14912      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14913      * Defaults to the value of the {@link #defaultSortable} property.
14914      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14915      */
14916     /**
14917      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14918      */
14919     /**
14920      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14921      */
14922     /**
14923      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14924      */
14925     /**
14926      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14927      */
14928     /**
14929      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14930      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14931      * default renderer uses the raw data value.
14932      */
14933     /**
14934      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14935      */
14936
14937     /**
14938      * Returns the id of the column at the specified index.
14939      * @param {Number} index The column index
14940      * @return {String} the id
14941      */
14942     getColumnId : function(index){
14943         return this.config[index].id;
14944     },
14945
14946     /**
14947      * Returns the column for a specified id.
14948      * @param {String} id The column id
14949      * @return {Object} the column
14950      */
14951     getColumnById : function(id){
14952         return this.lookup[id];
14953     },
14954
14955     
14956     /**
14957      * Returns the column for a specified dataIndex.
14958      * @param {String} dataIndex The column dataIndex
14959      * @return {Object|Boolean} the column or false if not found
14960      */
14961     getColumnByDataIndex: function(dataIndex){
14962         var index = this.findColumnIndex(dataIndex);
14963         return index > -1 ? this.config[index] : false;
14964     },
14965     
14966     /**
14967      * Returns the index for a specified column id.
14968      * @param {String} id The column id
14969      * @return {Number} the index, or -1 if not found
14970      */
14971     getIndexById : function(id){
14972         for(var i = 0, len = this.config.length; i < len; i++){
14973             if(this.config[i].id == id){
14974                 return i;
14975             }
14976         }
14977         return -1;
14978     },
14979     
14980     /**
14981      * Returns the index for a specified column dataIndex.
14982      * @param {String} dataIndex The column dataIndex
14983      * @return {Number} the index, or -1 if not found
14984      */
14985     
14986     findColumnIndex : function(dataIndex){
14987         for(var i = 0, len = this.config.length; i < len; i++){
14988             if(this.config[i].dataIndex == dataIndex){
14989                 return i;
14990             }
14991         }
14992         return -1;
14993     },
14994     
14995     
14996     moveColumn : function(oldIndex, newIndex){
14997         var c = this.config[oldIndex];
14998         this.config.splice(oldIndex, 1);
14999         this.config.splice(newIndex, 0, c);
15000         this.dataMap = null;
15001         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15002     },
15003
15004     isLocked : function(colIndex){
15005         return this.config[colIndex].locked === true;
15006     },
15007
15008     setLocked : function(colIndex, value, suppressEvent){
15009         if(this.isLocked(colIndex) == value){
15010             return;
15011         }
15012         this.config[colIndex].locked = value;
15013         if(!suppressEvent){
15014             this.fireEvent("columnlockchange", this, colIndex, value);
15015         }
15016     },
15017
15018     getTotalLockedWidth : function(){
15019         var totalWidth = 0;
15020         for(var i = 0; i < this.config.length; i++){
15021             if(this.isLocked(i) && !this.isHidden(i)){
15022                 this.totalWidth += this.getColumnWidth(i);
15023             }
15024         }
15025         return totalWidth;
15026     },
15027
15028     getLockedCount : function(){
15029         for(var i = 0, len = this.config.length; i < len; i++){
15030             if(!this.isLocked(i)){
15031                 return i;
15032             }
15033         }
15034     },
15035
15036     /**
15037      * Returns the number of columns.
15038      * @return {Number}
15039      */
15040     getColumnCount : function(visibleOnly){
15041         if(visibleOnly === true){
15042             var c = 0;
15043             for(var i = 0, len = this.config.length; i < len; i++){
15044                 if(!this.isHidden(i)){
15045                     c++;
15046                 }
15047             }
15048             return c;
15049         }
15050         return this.config.length;
15051     },
15052
15053     /**
15054      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15055      * @param {Function} fn
15056      * @param {Object} scope (optional)
15057      * @return {Array} result
15058      */
15059     getColumnsBy : function(fn, scope){
15060         var r = [];
15061         for(var i = 0, len = this.config.length; i < len; i++){
15062             var c = this.config[i];
15063             if(fn.call(scope||this, c, i) === true){
15064                 r[r.length] = c;
15065             }
15066         }
15067         return r;
15068     },
15069
15070     /**
15071      * Returns true if the specified column is sortable.
15072      * @param {Number} col The column index
15073      * @return {Boolean}
15074      */
15075     isSortable : function(col){
15076         if(typeof this.config[col].sortable == "undefined"){
15077             return this.defaultSortable;
15078         }
15079         return this.config[col].sortable;
15080     },
15081
15082     /**
15083      * Returns the rendering (formatting) function defined for the column.
15084      * @param {Number} col The column index.
15085      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15086      */
15087     getRenderer : function(col){
15088         if(!this.config[col].renderer){
15089             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15090         }
15091         return this.config[col].renderer;
15092     },
15093
15094     /**
15095      * Sets the rendering (formatting) function for a column.
15096      * @param {Number} col The column index
15097      * @param {Function} fn The function to use to process the cell's raw data
15098      * to return HTML markup for the grid view. The render function is called with
15099      * the following parameters:<ul>
15100      * <li>Data value.</li>
15101      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15102      * <li>css A CSS style string to apply to the table cell.</li>
15103      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15104      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15105      * <li>Row index</li>
15106      * <li>Column index</li>
15107      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15108      */
15109     setRenderer : function(col, fn){
15110         this.config[col].renderer = fn;
15111     },
15112
15113     /**
15114      * Returns the width for the specified column.
15115      * @param {Number} col The column index
15116      * @return {Number}
15117      */
15118     getColumnWidth : function(col){
15119         return this.config[col].width * 1 || this.defaultWidth;
15120     },
15121
15122     /**
15123      * Sets the width for a column.
15124      * @param {Number} col The column index
15125      * @param {Number} width The new width
15126      */
15127     setColumnWidth : function(col, width, suppressEvent){
15128         this.config[col].width = width;
15129         this.totalWidth = null;
15130         if(!suppressEvent){
15131              this.fireEvent("widthchange", this, col, width);
15132         }
15133     },
15134
15135     /**
15136      * Returns the total width of all columns.
15137      * @param {Boolean} includeHidden True to include hidden column widths
15138      * @return {Number}
15139      */
15140     getTotalWidth : function(includeHidden){
15141         if(!this.totalWidth){
15142             this.totalWidth = 0;
15143             for(var i = 0, len = this.config.length; i < len; i++){
15144                 if(includeHidden || !this.isHidden(i)){
15145                     this.totalWidth += this.getColumnWidth(i);
15146                 }
15147             }
15148         }
15149         return this.totalWidth;
15150     },
15151
15152     /**
15153      * Returns the header for the specified column.
15154      * @param {Number} col The column index
15155      * @return {String}
15156      */
15157     getColumnHeader : function(col){
15158         return this.config[col].header;
15159     },
15160
15161     /**
15162      * Sets the header for a column.
15163      * @param {Number} col The column index
15164      * @param {String} header The new header
15165      */
15166     setColumnHeader : function(col, header){
15167         this.config[col].header = header;
15168         this.fireEvent("headerchange", this, col, header);
15169     },
15170
15171     /**
15172      * Returns the tooltip for the specified column.
15173      * @param {Number} col The column index
15174      * @return {String}
15175      */
15176     getColumnTooltip : function(col){
15177             return this.config[col].tooltip;
15178     },
15179     /**
15180      * Sets the tooltip for a column.
15181      * @param {Number} col The column index
15182      * @param {String} tooltip The new tooltip
15183      */
15184     setColumnTooltip : function(col, tooltip){
15185             this.config[col].tooltip = tooltip;
15186     },
15187
15188     /**
15189      * Returns the dataIndex for the specified column.
15190      * @param {Number} col The column index
15191      * @return {Number}
15192      */
15193     getDataIndex : function(col){
15194         return this.config[col].dataIndex;
15195     },
15196
15197     /**
15198      * Sets the dataIndex for a column.
15199      * @param {Number} col The column index
15200      * @param {Number} dataIndex The new dataIndex
15201      */
15202     setDataIndex : function(col, dataIndex){
15203         this.config[col].dataIndex = dataIndex;
15204     },
15205
15206     
15207     
15208     /**
15209      * Returns true if the cell is editable.
15210      * @param {Number} colIndex The column index
15211      * @param {Number} rowIndex The row index
15212      * @return {Boolean}
15213      */
15214     isCellEditable : function(colIndex, rowIndex){
15215         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15216     },
15217
15218     /**
15219      * Returns the editor defined for the cell/column.
15220      * return false or null to disable editing.
15221      * @param {Number} colIndex The column index
15222      * @param {Number} rowIndex The row index
15223      * @return {Object}
15224      */
15225     getCellEditor : function(colIndex, rowIndex){
15226         return this.config[colIndex].editor;
15227     },
15228
15229     /**
15230      * Sets if a column is editable.
15231      * @param {Number} col The column index
15232      * @param {Boolean} editable True if the column is editable
15233      */
15234     setEditable : function(col, editable){
15235         this.config[col].editable = editable;
15236     },
15237
15238
15239     /**
15240      * Returns true if the column is hidden.
15241      * @param {Number} colIndex The column index
15242      * @return {Boolean}
15243      */
15244     isHidden : function(colIndex){
15245         return this.config[colIndex].hidden;
15246     },
15247
15248
15249     /**
15250      * Returns true if the column width cannot be changed
15251      */
15252     isFixed : function(colIndex){
15253         return this.config[colIndex].fixed;
15254     },
15255
15256     /**
15257      * Returns true if the column can be resized
15258      * @return {Boolean}
15259      */
15260     isResizable : function(colIndex){
15261         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15262     },
15263     /**
15264      * Sets if a column is hidden.
15265      * @param {Number} colIndex The column index
15266      * @param {Boolean} hidden True if the column is hidden
15267      */
15268     setHidden : function(colIndex, hidden){
15269         this.config[colIndex].hidden = hidden;
15270         this.totalWidth = null;
15271         this.fireEvent("hiddenchange", this, colIndex, hidden);
15272     },
15273
15274     /**
15275      * Sets the editor for a column.
15276      * @param {Number} col The column index
15277      * @param {Object} editor The editor object
15278      */
15279     setEditor : function(col, editor){
15280         this.config[col].editor = editor;
15281     }
15282 });
15283
15284 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15285         if(typeof value == "string" && value.length < 1){
15286             return "&#160;";
15287         }
15288         return value;
15289 };
15290
15291 // Alias for backwards compatibility
15292 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15293
15294 /**
15295  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15296  * @class Roo.bootstrap.Table.RowSelectionModel
15297  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15298  * It supports multiple selections and keyboard selection/navigation. 
15299  * @constructor
15300  * @param {Object} config
15301  */
15302
15303 Roo.bootstrap.Table.RowSelectionModel = function(config){
15304     Roo.apply(this, config);
15305     this.selections = new Roo.util.MixedCollection(false, function(o){
15306         return o.id;
15307     });
15308
15309     this.last = false;
15310     this.lastActive = false;
15311
15312     this.addEvents({
15313         /**
15314              * @event selectionchange
15315              * Fires when the selection changes
15316              * @param {SelectionModel} this
15317              */
15318             "selectionchange" : true,
15319         /**
15320              * @event afterselectionchange
15321              * Fires after the selection changes (eg. by key press or clicking)
15322              * @param {SelectionModel} this
15323              */
15324             "afterselectionchange" : true,
15325         /**
15326              * @event beforerowselect
15327              * Fires when a row is selected being selected, return false to cancel.
15328              * @param {SelectionModel} this
15329              * @param {Number} rowIndex The selected index
15330              * @param {Boolean} keepExisting False if other selections will be cleared
15331              */
15332             "beforerowselect" : true,
15333         /**
15334              * @event rowselect
15335              * Fires when a row is selected.
15336              * @param {SelectionModel} this
15337              * @param {Number} rowIndex The selected index
15338              * @param {Roo.data.Record} r The record
15339              */
15340             "rowselect" : true,
15341         /**
15342              * @event rowdeselect
15343              * Fires when a row is deselected.
15344              * @param {SelectionModel} this
15345              * @param {Number} rowIndex The selected index
15346              */
15347         "rowdeselect" : true
15348     });
15349     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15350     this.locked = false;
15351 };
15352
15353 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15354     /**
15355      * @cfg {Boolean} singleSelect
15356      * True to allow selection of only one row at a time (defaults to false)
15357      */
15358     singleSelect : false,
15359
15360     // private
15361     initEvents : function(){
15362
15363         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15364             this.grid.on("mousedown", this.handleMouseDown, this);
15365         }else{ // allow click to work like normal
15366             this.grid.on("rowclick", this.handleDragableRowClick, this);
15367         }
15368
15369         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15370             "up" : function(e){
15371                 if(!e.shiftKey){
15372                     this.selectPrevious(e.shiftKey);
15373                 }else if(this.last !== false && this.lastActive !== false){
15374                     var last = this.last;
15375                     this.selectRange(this.last,  this.lastActive-1);
15376                     this.grid.getView().focusRow(this.lastActive);
15377                     if(last !== false){
15378                         this.last = last;
15379                     }
15380                 }else{
15381                     this.selectFirstRow();
15382                 }
15383                 this.fireEvent("afterselectionchange", this);
15384             },
15385             "down" : function(e){
15386                 if(!e.shiftKey){
15387                     this.selectNext(e.shiftKey);
15388                 }else if(this.last !== false && this.lastActive !== false){
15389                     var last = this.last;
15390                     this.selectRange(this.last,  this.lastActive+1);
15391                     this.grid.getView().focusRow(this.lastActive);
15392                     if(last !== false){
15393                         this.last = last;
15394                     }
15395                 }else{
15396                     this.selectFirstRow();
15397                 }
15398                 this.fireEvent("afterselectionchange", this);
15399             },
15400             scope: this
15401         });
15402
15403         var view = this.grid.view;
15404         view.on("refresh", this.onRefresh, this);
15405         view.on("rowupdated", this.onRowUpdated, this);
15406         view.on("rowremoved", this.onRemove, this);
15407     },
15408
15409     // private
15410     onRefresh : function(){
15411         var ds = this.grid.dataSource, i, v = this.grid.view;
15412         var s = this.selections;
15413         s.each(function(r){
15414             if((i = ds.indexOfId(r.id)) != -1){
15415                 v.onRowSelect(i);
15416             }else{
15417                 s.remove(r);
15418             }
15419         });
15420     },
15421
15422     // private
15423     onRemove : function(v, index, r){
15424         this.selections.remove(r);
15425     },
15426
15427     // private
15428     onRowUpdated : function(v, index, r){
15429         if(this.isSelected(r)){
15430             v.onRowSelect(index);
15431         }
15432     },
15433
15434     /**
15435      * Select records.
15436      * @param {Array} records The records to select
15437      * @param {Boolean} keepExisting (optional) True to keep existing selections
15438      */
15439     selectRecords : function(records, keepExisting){
15440         if(!keepExisting){
15441             this.clearSelections();
15442         }
15443         var ds = this.grid.dataSource;
15444         for(var i = 0, len = records.length; i < len; i++){
15445             this.selectRow(ds.indexOf(records[i]), true);
15446         }
15447     },
15448
15449     /**
15450      * Gets the number of selected rows.
15451      * @return {Number}
15452      */
15453     getCount : function(){
15454         return this.selections.length;
15455     },
15456
15457     /**
15458      * Selects the first row in the grid.
15459      */
15460     selectFirstRow : function(){
15461         this.selectRow(0);
15462     },
15463
15464     /**
15465      * Select the last row.
15466      * @param {Boolean} keepExisting (optional) True to keep existing selections
15467      */
15468     selectLastRow : function(keepExisting){
15469         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15470     },
15471
15472     /**
15473      * Selects the row immediately following the last selected row.
15474      * @param {Boolean} keepExisting (optional) True to keep existing selections
15475      */
15476     selectNext : function(keepExisting){
15477         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15478             this.selectRow(this.last+1, keepExisting);
15479             this.grid.getView().focusRow(this.last);
15480         }
15481     },
15482
15483     /**
15484      * Selects the row that precedes the last selected row.
15485      * @param {Boolean} keepExisting (optional) True to keep existing selections
15486      */
15487     selectPrevious : function(keepExisting){
15488         if(this.last){
15489             this.selectRow(this.last-1, keepExisting);
15490             this.grid.getView().focusRow(this.last);
15491         }
15492     },
15493
15494     /**
15495      * Returns the selected records
15496      * @return {Array} Array of selected records
15497      */
15498     getSelections : function(){
15499         return [].concat(this.selections.items);
15500     },
15501
15502     /**
15503      * Returns the first selected record.
15504      * @return {Record}
15505      */
15506     getSelected : function(){
15507         return this.selections.itemAt(0);
15508     },
15509
15510
15511     /**
15512      * Clears all selections.
15513      */
15514     clearSelections : function(fast){
15515         if(this.locked) return;
15516         if(fast !== true){
15517             var ds = this.grid.dataSource;
15518             var s = this.selections;
15519             s.each(function(r){
15520                 this.deselectRow(ds.indexOfId(r.id));
15521             }, this);
15522             s.clear();
15523         }else{
15524             this.selections.clear();
15525         }
15526         this.last = false;
15527     },
15528
15529
15530     /**
15531      * Selects all rows.
15532      */
15533     selectAll : function(){
15534         if(this.locked) return;
15535         this.selections.clear();
15536         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15537             this.selectRow(i, true);
15538         }
15539     },
15540
15541     /**
15542      * Returns True if there is a selection.
15543      * @return {Boolean}
15544      */
15545     hasSelection : function(){
15546         return this.selections.length > 0;
15547     },
15548
15549     /**
15550      * Returns True if the specified row is selected.
15551      * @param {Number/Record} record The record or index of the record to check
15552      * @return {Boolean}
15553      */
15554     isSelected : function(index){
15555         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15556         return (r && this.selections.key(r.id) ? true : false);
15557     },
15558
15559     /**
15560      * Returns True if the specified record id is selected.
15561      * @param {String} id The id of record to check
15562      * @return {Boolean}
15563      */
15564     isIdSelected : function(id){
15565         return (this.selections.key(id) ? true : false);
15566     },
15567
15568     // private
15569     handleMouseDown : function(e, t){
15570         var view = this.grid.getView(), rowIndex;
15571         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15572             return;
15573         };
15574         if(e.shiftKey && this.last !== false){
15575             var last = this.last;
15576             this.selectRange(last, rowIndex, e.ctrlKey);
15577             this.last = last; // reset the last
15578             view.focusRow(rowIndex);
15579         }else{
15580             var isSelected = this.isSelected(rowIndex);
15581             if(e.button !== 0 && isSelected){
15582                 view.focusRow(rowIndex);
15583             }else if(e.ctrlKey && isSelected){
15584                 this.deselectRow(rowIndex);
15585             }else if(!isSelected){
15586                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15587                 view.focusRow(rowIndex);
15588             }
15589         }
15590         this.fireEvent("afterselectionchange", this);
15591     },
15592     // private
15593     handleDragableRowClick :  function(grid, rowIndex, e) 
15594     {
15595         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15596             this.selectRow(rowIndex, false);
15597             grid.view.focusRow(rowIndex);
15598              this.fireEvent("afterselectionchange", this);
15599         }
15600     },
15601     
15602     /**
15603      * Selects multiple rows.
15604      * @param {Array} rows Array of the indexes of the row to select
15605      * @param {Boolean} keepExisting (optional) True to keep existing selections
15606      */
15607     selectRows : function(rows, keepExisting){
15608         if(!keepExisting){
15609             this.clearSelections();
15610         }
15611         for(var i = 0, len = rows.length; i < len; i++){
15612             this.selectRow(rows[i], true);
15613         }
15614     },
15615
15616     /**
15617      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15618      * @param {Number} startRow The index of the first row in the range
15619      * @param {Number} endRow The index of the last row in the range
15620      * @param {Boolean} keepExisting (optional) True to retain existing selections
15621      */
15622     selectRange : function(startRow, endRow, keepExisting){
15623         if(this.locked) return;
15624         if(!keepExisting){
15625             this.clearSelections();
15626         }
15627         if(startRow <= endRow){
15628             for(var i = startRow; i <= endRow; i++){
15629                 this.selectRow(i, true);
15630             }
15631         }else{
15632             for(var i = startRow; i >= endRow; i--){
15633                 this.selectRow(i, true);
15634             }
15635         }
15636     },
15637
15638     /**
15639      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15640      * @param {Number} startRow The index of the first row in the range
15641      * @param {Number} endRow The index of the last row in the range
15642      */
15643     deselectRange : function(startRow, endRow, preventViewNotify){
15644         if(this.locked) return;
15645         for(var i = startRow; i <= endRow; i++){
15646             this.deselectRow(i, preventViewNotify);
15647         }
15648     },
15649
15650     /**
15651      * Selects a row.
15652      * @param {Number} row The index of the row to select
15653      * @param {Boolean} keepExisting (optional) True to keep existing selections
15654      */
15655     selectRow : function(index, keepExisting, preventViewNotify){
15656         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15657         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15658             if(!keepExisting || this.singleSelect){
15659                 this.clearSelections();
15660             }
15661             var r = this.grid.dataSource.getAt(index);
15662             this.selections.add(r);
15663             this.last = this.lastActive = index;
15664             if(!preventViewNotify){
15665                 this.grid.getView().onRowSelect(index);
15666             }
15667             this.fireEvent("rowselect", this, index, r);
15668             this.fireEvent("selectionchange", this);
15669         }
15670     },
15671
15672     /**
15673      * Deselects a row.
15674      * @param {Number} row The index of the row to deselect
15675      */
15676     deselectRow : function(index, preventViewNotify){
15677         if(this.locked) return;
15678         if(this.last == index){
15679             this.last = false;
15680         }
15681         if(this.lastActive == index){
15682             this.lastActive = false;
15683         }
15684         var r = this.grid.dataSource.getAt(index);
15685         this.selections.remove(r);
15686         if(!preventViewNotify){
15687             this.grid.getView().onRowDeselect(index);
15688         }
15689         this.fireEvent("rowdeselect", this, index);
15690         this.fireEvent("selectionchange", this);
15691     },
15692
15693     // private
15694     restoreLast : function(){
15695         if(this._last){
15696             this.last = this._last;
15697         }
15698     },
15699
15700     // private
15701     acceptsNav : function(row, col, cm){
15702         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15703     },
15704
15705     // private
15706     onEditorKey : function(field, e){
15707         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15708         if(k == e.TAB){
15709             e.stopEvent();
15710             ed.completeEdit();
15711             if(e.shiftKey){
15712                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15713             }else{
15714                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15715             }
15716         }else if(k == e.ENTER && !e.ctrlKey){
15717             e.stopEvent();
15718             ed.completeEdit();
15719             if(e.shiftKey){
15720                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15721             }else{
15722                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15723             }
15724         }else if(k == e.ESC){
15725             ed.cancelEdit();
15726         }
15727         if(newCell){
15728             g.startEditing(newCell[0], newCell[1]);
15729         }
15730     }
15731 });/*
15732  * - LGPL
15733  *
15734  * element
15735  * 
15736  */
15737
15738 /**
15739  * @class Roo.bootstrap.MessageBar
15740  * @extends Roo.bootstrap.Component
15741  * Bootstrap MessageBar class
15742  * @cfg {String} html contents of the MessageBar
15743  * @cfg {String} weight (info | success | warning | danger) default info
15744  * @cfg {String} beforeClass insert the bar before the given class
15745  * @cfg {Boolean} closable (true | false) default false
15746  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15747  * 
15748  * @constructor
15749  * Create a new Element
15750  * @param {Object} config The config object
15751  */
15752
15753 Roo.bootstrap.MessageBar = function(config){
15754     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15755 };
15756
15757 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15758     
15759     html: '',
15760     weight: 'info',
15761     closable: false,
15762     fixed: false,
15763     beforeClass: 'bootstrap-sticky-wrap',
15764     
15765     getAutoCreate : function(){
15766         
15767         var cfg = {
15768             tag: 'div',
15769             cls: 'alert alert-dismissable alert-' + this.weight,
15770             cn: [
15771                 {
15772                     tag: 'span',
15773                     cls: 'message',
15774                     html: this.html || ''
15775                 }
15776             ]
15777         }
15778         
15779         if(this.fixed){
15780             cfg.cls += ' alert-messages-fixed';
15781         }
15782         
15783         if(this.closable){
15784             cfg.cn.push({
15785                 tag: 'button',
15786                 cls: 'close',
15787                 html: 'x'
15788             });
15789         }
15790         
15791         return cfg;
15792     },
15793     
15794     onRender : function(ct, position)
15795     {
15796         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15797         
15798         if(!this.el){
15799             var cfg = Roo.apply({},  this.getAutoCreate());
15800             cfg.id = Roo.id();
15801             
15802             if (this.cls) {
15803                 cfg.cls += ' ' + this.cls;
15804             }
15805             if (this.style) {
15806                 cfg.style = this.style;
15807             }
15808             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15809             
15810             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15811         }
15812         
15813         this.el.select('>button.close').on('click', this.hide, this);
15814         
15815     },
15816     
15817     show : function()
15818     {
15819         if (!this.rendered) {
15820             this.render();
15821         }
15822         
15823         this.el.show();
15824         
15825         this.fireEvent('show', this);
15826         
15827     },
15828     
15829     hide : function()
15830     {
15831         if (!this.rendered) {
15832             this.render();
15833         }
15834         
15835         this.el.hide();
15836         
15837         this.fireEvent('hide', this);
15838     },
15839     
15840     update : function()
15841     {
15842 //        var e = this.el.dom.firstChild;
15843 //        
15844 //        if(this.closable){
15845 //            e = e.nextSibling;
15846 //        }
15847 //        
15848 //        e.data = this.html || '';
15849
15850         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15851     }
15852    
15853 });
15854
15855  
15856
15857