Roo/bootstrap/CheckBox.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                     var value = '';
3025                     var id = Roo.id();
3026                     
3027                     if(typeof(renderer) !== 'undefined'){
3028                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3029                     }
3030                     
3031                     if(typeof(value) === 'object'){
3032                         renders.push({
3033                             id : id,
3034                             cfg : value 
3035                         })
3036                     }
3037                     
3038                     row.cn.push({
3039                         tag: 'td',
3040                         id: id,
3041                         html: (typeof(value) === 'object') ? '' : value
3042                     })
3043                    
3044                 }
3045                 
3046                 tbody.createChild(row);
3047                 
3048             });
3049         }
3050         
3051         
3052         if(renders.length){
3053             var _this = this;
3054             Roo.each(renders, function(r){
3055                 _this.renderColumn(r);
3056             })
3057         }
3058 //        
3059 //        if(this.loadMask){
3060 //            this.maskEl.hide();
3061 //        }
3062     },
3063     
3064     onBeforeLoad : function()
3065     {
3066         Roo.log('ds onBeforeLoad');
3067         
3068         this.clear();
3069         
3070 //        if(this.loadMask){
3071 //            this.maskEl.show();
3072 //        }
3073     },
3074     
3075     clear : function()
3076     {
3077         this.el.select('tbody', true).first().dom.innerHTML = '';
3078     },
3079     
3080     getSelectionModel : function(){
3081         if(!this.selModel){
3082             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3083         }
3084         return this.selModel;
3085     },
3086     
3087     renderColumn : function(r)
3088     {
3089         var _this = this;
3090         r.cfg.render(Roo.get(r.id));
3091         
3092         if(r.cfg.cn){
3093             Roo.each(r.cfg.cn, function(c){
3094                 var child = {
3095                     id: r.id,
3096                     cfg: c
3097                 }
3098                 _this.renderColumn(child);
3099             })
3100         }
3101     }
3102    
3103 });
3104
3105  
3106
3107  /*
3108  * - LGPL
3109  *
3110  * table cell
3111  * 
3112  */
3113
3114 /**
3115  * @class Roo.bootstrap.TableCell
3116  * @extends Roo.bootstrap.Component
3117  * Bootstrap TableCell class
3118  * @cfg {String} html cell contain text
3119  * @cfg {String} cls cell class
3120  * @cfg {String} tag cell tag (td|th) default td
3121  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3122  * @cfg {String} align Aligns the content in a cell
3123  * @cfg {String} axis Categorizes cells
3124  * @cfg {String} bgcolor Specifies the background color of a cell
3125  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3126  * @cfg {Number} colspan Specifies the number of columns a cell should span
3127  * @cfg {String} headers Specifies one or more header cells a cell is related to
3128  * @cfg {Number} height Sets the height of a cell
3129  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3130  * @cfg {Number} rowspan Sets the number of rows a cell should span
3131  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3132  * @cfg {String} valign Vertical aligns the content in a cell
3133  * @cfg {Number} width Specifies the width of a cell
3134  * 
3135  * @constructor
3136  * Create a new TableCell
3137  * @param {Object} config The config object
3138  */
3139
3140 Roo.bootstrap.TableCell = function(config){
3141     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3142 };
3143
3144 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3145     
3146     html: false,
3147     cls: false,
3148     tag: false,
3149     abbr: false,
3150     align: false,
3151     axis: false,
3152     bgcolor: false,
3153     charoff: false,
3154     colspan: false,
3155     headers: false,
3156     height: false,
3157     nowrap: false,
3158     rowspan: false,
3159     scope: false,
3160     valign: false,
3161     width: false,
3162     
3163     
3164     getAutoCreate : function(){
3165         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3166         
3167         cfg = {
3168             tag: 'td'
3169         }
3170         
3171         if(this.tag){
3172             cfg.tag = this.tag;
3173         }
3174         
3175         if (this.html) {
3176             cfg.html=this.html
3177         }
3178         if (this.cls) {
3179             cfg.cls=this.cls
3180         }
3181         if (this.abbr) {
3182             cfg.abbr=this.abbr
3183         }
3184         if (this.align) {
3185             cfg.align=this.align
3186         }
3187         if (this.axis) {
3188             cfg.axis=this.axis
3189         }
3190         if (this.bgcolor) {
3191             cfg.bgcolor=this.bgcolor
3192         }
3193         if (this.charoff) {
3194             cfg.charoff=this.charoff
3195         }
3196         if (this.colspan) {
3197             cfg.colspan=this.colspan
3198         }
3199         if (this.headers) {
3200             cfg.headers=this.headers
3201         }
3202         if (this.height) {
3203             cfg.height=this.height
3204         }
3205         if (this.nowrap) {
3206             cfg.nowrap=this.nowrap
3207         }
3208         if (this.rowspan) {
3209             cfg.rowspan=this.rowspan
3210         }
3211         if (this.scope) {
3212             cfg.scope=this.scope
3213         }
3214         if (this.valign) {
3215             cfg.valign=this.valign
3216         }
3217         if (this.width) {
3218             cfg.width=this.width
3219         }
3220         
3221         
3222         return cfg;
3223     }
3224    
3225 });
3226
3227  
3228
3229  /*
3230  * - LGPL
3231  *
3232  * table row
3233  * 
3234  */
3235
3236 /**
3237  * @class Roo.bootstrap.TableRow
3238  * @extends Roo.bootstrap.Component
3239  * Bootstrap TableRow class
3240  * @cfg {String} cls row class
3241  * @cfg {String} align Aligns the content in a table row
3242  * @cfg {String} bgcolor Specifies a background color for a table row
3243  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3244  * @cfg {String} valign Vertical aligns the content in a table row
3245  * 
3246  * @constructor
3247  * Create a new TableRow
3248  * @param {Object} config The config object
3249  */
3250
3251 Roo.bootstrap.TableRow = function(config){
3252     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3253 };
3254
3255 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3256     
3257     cls: false,
3258     align: false,
3259     bgcolor: false,
3260     charoff: false,
3261     valign: false,
3262     
3263     getAutoCreate : function(){
3264         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3265         
3266         cfg = {
3267             tag: 'tr'
3268         }
3269             
3270         if(this.cls){
3271             cfg.cls = this.cls;
3272         }
3273         if(this.align){
3274             cfg.align = this.align;
3275         }
3276         if(this.bgcolor){
3277             cfg.bgcolor = this.bgcolor;
3278         }
3279         if(this.charoff){
3280             cfg.charoff = this.charoff;
3281         }
3282         if(this.valign){
3283             cfg.valign = this.valign;
3284         }
3285         
3286         return cfg;
3287     }
3288    
3289 });
3290
3291  
3292
3293  /*
3294  * - LGPL
3295  *
3296  * table body
3297  * 
3298  */
3299
3300 /**
3301  * @class Roo.bootstrap.TableBody
3302  * @extends Roo.bootstrap.Component
3303  * Bootstrap TableBody class
3304  * @cfg {String} cls element class
3305  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3306  * @cfg {String} align Aligns the content inside the element
3307  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3308  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3309  * 
3310  * @constructor
3311  * Create a new TableBody
3312  * @param {Object} config The config object
3313  */
3314
3315 Roo.bootstrap.TableBody = function(config){
3316     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3317 };
3318
3319 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3320     
3321     cls: false,
3322     tag: false,
3323     align: false,
3324     charoff: false,
3325     valign: false,
3326     
3327     getAutoCreate : function(){
3328         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3329         
3330         cfg = {
3331             tag: 'tbody'
3332         }
3333             
3334         if (this.cls) {
3335             cfg.cls=this.cls
3336         }
3337         if(this.tag){
3338             cfg.tag = this.tag;
3339         }
3340         
3341         if(this.align){
3342             cfg.align = this.align;
3343         }
3344         if(this.charoff){
3345             cfg.charoff = this.charoff;
3346         }
3347         if(this.valign){
3348             cfg.valign = this.valign;
3349         }
3350         
3351         return cfg;
3352     }
3353     
3354     
3355 //    initEvents : function()
3356 //    {
3357 //        
3358 //        if(!this.store){
3359 //            return;
3360 //        }
3361 //        
3362 //        this.store = Roo.factory(this.store, Roo.data);
3363 //        this.store.on('load', this.onLoad, this);
3364 //        
3365 //        this.store.load();
3366 //        
3367 //    },
3368 //    
3369 //    onLoad: function () 
3370 //    {   
3371 //        this.fireEvent('load', this);
3372 //    }
3373 //    
3374 //   
3375 });
3376
3377  
3378
3379  /*
3380  * Based on:
3381  * Ext JS Library 1.1.1
3382  * Copyright(c) 2006-2007, Ext JS, LLC.
3383  *
3384  * Originally Released Under LGPL - original licence link has changed is not relivant.
3385  *
3386  * Fork - LGPL
3387  * <script type="text/javascript">
3388  */
3389
3390 // as we use this in bootstrap.
3391 Roo.namespace('Roo.form');
3392  /**
3393  * @class Roo.form.Action
3394  * Internal Class used to handle form actions
3395  * @constructor
3396  * @param {Roo.form.BasicForm} el The form element or its id
3397  * @param {Object} config Configuration options
3398  */
3399
3400  
3401  
3402 // define the action interface
3403 Roo.form.Action = function(form, options){
3404     this.form = form;
3405     this.options = options || {};
3406 };
3407 /**
3408  * Client Validation Failed
3409  * @const 
3410  */
3411 Roo.form.Action.CLIENT_INVALID = 'client';
3412 /**
3413  * Server Validation Failed
3414  * @const 
3415  */
3416 Roo.form.Action.SERVER_INVALID = 'server';
3417  /**
3418  * Connect to Server Failed
3419  * @const 
3420  */
3421 Roo.form.Action.CONNECT_FAILURE = 'connect';
3422 /**
3423  * Reading Data from Server Failed
3424  * @const 
3425  */
3426 Roo.form.Action.LOAD_FAILURE = 'load';
3427
3428 Roo.form.Action.prototype = {
3429     type : 'default',
3430     failureType : undefined,
3431     response : undefined,
3432     result : undefined,
3433
3434     // interface method
3435     run : function(options){
3436
3437     },
3438
3439     // interface method
3440     success : function(response){
3441
3442     },
3443
3444     // interface method
3445     handleResponse : function(response){
3446
3447     },
3448
3449     // default connection failure
3450     failure : function(response){
3451         
3452         this.response = response;
3453         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3454         this.form.afterAction(this, false);
3455     },
3456
3457     processResponse : function(response){
3458         this.response = response;
3459         if(!response.responseText){
3460             return true;
3461         }
3462         this.result = this.handleResponse(response);
3463         return this.result;
3464     },
3465
3466     // utility functions used internally
3467     getUrl : function(appendParams){
3468         var url = this.options.url || this.form.url || this.form.el.dom.action;
3469         if(appendParams){
3470             var p = this.getParams();
3471             if(p){
3472                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3473             }
3474         }
3475         return url;
3476     },
3477
3478     getMethod : function(){
3479         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3480     },
3481
3482     getParams : function(){
3483         var bp = this.form.baseParams;
3484         var p = this.options.params;
3485         if(p){
3486             if(typeof p == "object"){
3487                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3488             }else if(typeof p == 'string' && bp){
3489                 p += '&' + Roo.urlEncode(bp);
3490             }
3491         }else if(bp){
3492             p = Roo.urlEncode(bp);
3493         }
3494         return p;
3495     },
3496
3497     createCallback : function(){
3498         return {
3499             success: this.success,
3500             failure: this.failure,
3501             scope: this,
3502             timeout: (this.form.timeout*1000),
3503             upload: this.form.fileUpload ? this.success : undefined
3504         };
3505     }
3506 };
3507
3508 Roo.form.Action.Submit = function(form, options){
3509     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3510 };
3511
3512 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3513     type : 'submit',
3514
3515     haveProgress : false,
3516     uploadComplete : false,
3517     
3518     // uploadProgress indicator.
3519     uploadProgress : function()
3520     {
3521         if (!this.form.progressUrl) {
3522             return;
3523         }
3524         
3525         if (!this.haveProgress) {
3526             Roo.MessageBox.progress("Uploading", "Uploading");
3527         }
3528         if (this.uploadComplete) {
3529            Roo.MessageBox.hide();
3530            return;
3531         }
3532         
3533         this.haveProgress = true;
3534    
3535         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3536         
3537         var c = new Roo.data.Connection();
3538         c.request({
3539             url : this.form.progressUrl,
3540             params: {
3541                 id : uid
3542             },
3543             method: 'GET',
3544             success : function(req){
3545                //console.log(data);
3546                 var rdata = false;
3547                 var edata;
3548                 try  {
3549                    rdata = Roo.decode(req.responseText)
3550                 } catch (e) {
3551                     Roo.log("Invalid data from server..");
3552                     Roo.log(edata);
3553                     return;
3554                 }
3555                 if (!rdata || !rdata.success) {
3556                     Roo.log(rdata);
3557                     Roo.MessageBox.alert(Roo.encode(rdata));
3558                     return;
3559                 }
3560                 var data = rdata.data;
3561                 
3562                 if (this.uploadComplete) {
3563                    Roo.MessageBox.hide();
3564                    return;
3565                 }
3566                    
3567                 if (data){
3568                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3569                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3570                     );
3571                 }
3572                 this.uploadProgress.defer(2000,this);
3573             },
3574        
3575             failure: function(data) {
3576                 Roo.log('progress url failed ');
3577                 Roo.log(data);
3578             },
3579             scope : this
3580         });
3581            
3582     },
3583     
3584     
3585     run : function()
3586     {
3587         // run get Values on the form, so it syncs any secondary forms.
3588         this.form.getValues();
3589         
3590         var o = this.options;
3591         var method = this.getMethod();
3592         var isPost = method == 'POST';
3593         if(o.clientValidation === false || this.form.isValid()){
3594             
3595             if (this.form.progressUrl) {
3596                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3597                     (new Date() * 1) + '' + Math.random());
3598                     
3599             } 
3600             
3601             
3602             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3603                 form:this.form.el.dom,
3604                 url:this.getUrl(!isPost),
3605                 method: method,
3606                 params:isPost ? this.getParams() : null,
3607                 isUpload: this.form.fileUpload
3608             }));
3609             
3610             this.uploadProgress();
3611
3612         }else if (o.clientValidation !== false){ // client validation failed
3613             this.failureType = Roo.form.Action.CLIENT_INVALID;
3614             this.form.afterAction(this, false);
3615         }
3616     },
3617
3618     success : function(response)
3619     {
3620         this.uploadComplete= true;
3621         if (this.haveProgress) {
3622             Roo.MessageBox.hide();
3623         }
3624         
3625         
3626         var result = this.processResponse(response);
3627         if(result === true || result.success){
3628             this.form.afterAction(this, true);
3629             return;
3630         }
3631         if(result.errors){
3632             this.form.markInvalid(result.errors);
3633             this.failureType = Roo.form.Action.SERVER_INVALID;
3634         }
3635         this.form.afterAction(this, false);
3636     },
3637     failure : function(response)
3638     {
3639         this.uploadComplete= true;
3640         if (this.haveProgress) {
3641             Roo.MessageBox.hide();
3642         }
3643         
3644         this.response = response;
3645         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3646         this.form.afterAction(this, false);
3647     },
3648     
3649     handleResponse : function(response){
3650         if(this.form.errorReader){
3651             var rs = this.form.errorReader.read(response);
3652             var errors = [];
3653             if(rs.records){
3654                 for(var i = 0, len = rs.records.length; i < len; i++) {
3655                     var r = rs.records[i];
3656                     errors[i] = r.data;
3657                 }
3658             }
3659             if(errors.length < 1){
3660                 errors = null;
3661             }
3662             return {
3663                 success : rs.success,
3664                 errors : errors
3665             };
3666         }
3667         var ret = false;
3668         try {
3669             ret = Roo.decode(response.responseText);
3670         } catch (e) {
3671             ret = {
3672                 success: false,
3673                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3674                 errors : []
3675             };
3676         }
3677         return ret;
3678         
3679     }
3680 });
3681
3682
3683 Roo.form.Action.Load = function(form, options){
3684     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3685     this.reader = this.form.reader;
3686 };
3687
3688 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3689     type : 'load',
3690
3691     run : function(){
3692         
3693         Roo.Ajax.request(Roo.apply(
3694                 this.createCallback(), {
3695                     method:this.getMethod(),
3696                     url:this.getUrl(false),
3697                     params:this.getParams()
3698         }));
3699     },
3700
3701     success : function(response){
3702         
3703         var result = this.processResponse(response);
3704         if(result === true || !result.success || !result.data){
3705             this.failureType = Roo.form.Action.LOAD_FAILURE;
3706             this.form.afterAction(this, false);
3707             return;
3708         }
3709         this.form.clearInvalid();
3710         this.form.setValues(result.data);
3711         this.form.afterAction(this, true);
3712     },
3713
3714     handleResponse : function(response){
3715         if(this.form.reader){
3716             var rs = this.form.reader.read(response);
3717             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3718             return {
3719                 success : rs.success,
3720                 data : data
3721             };
3722         }
3723         return Roo.decode(response.responseText);
3724     }
3725 });
3726
3727 Roo.form.Action.ACTION_TYPES = {
3728     'load' : Roo.form.Action.Load,
3729     'submit' : Roo.form.Action.Submit
3730 };/*
3731  * - LGPL
3732  *
3733  * form
3734  * 
3735  */
3736
3737 /**
3738  * @class Roo.bootstrap.Form
3739  * @extends Roo.bootstrap.Component
3740  * Bootstrap Form class
3741  * @cfg {String} method  GET | POST (default POST)
3742  * @cfg {String} labelAlign top | left (default top)
3743   * @cfg {String} align left  | right - for navbars
3744
3745  * 
3746  * @constructor
3747  * Create a new Form
3748  * @param {Object} config The config object
3749  */
3750
3751
3752 Roo.bootstrap.Form = function(config){
3753     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3754     this.addEvents({
3755         /**
3756          * @event clientvalidation
3757          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3758          * @param {Form} this
3759          * @param {Boolean} valid true if the form has passed client-side validation
3760          */
3761         clientvalidation: true,
3762         /**
3763          * @event beforeaction
3764          * Fires before any action is performed. Return false to cancel the action.
3765          * @param {Form} this
3766          * @param {Action} action The action to be performed
3767          */
3768         beforeaction: true,
3769         /**
3770          * @event actionfailed
3771          * Fires when an action fails.
3772          * @param {Form} this
3773          * @param {Action} action The action that failed
3774          */
3775         actionfailed : true,
3776         /**
3777          * @event actioncomplete
3778          * Fires when an action is completed.
3779          * @param {Form} this
3780          * @param {Action} action The action that completed
3781          */
3782         actioncomplete : true
3783     });
3784     
3785 };
3786
3787 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3788       
3789      /**
3790      * @cfg {String} method
3791      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3792      */
3793     method : 'POST',
3794     /**
3795      * @cfg {String} url
3796      * The URL to use for form actions if one isn't supplied in the action options.
3797      */
3798     /**
3799      * @cfg {Boolean} fileUpload
3800      * Set to true if this form is a file upload.
3801      */
3802      
3803     /**
3804      * @cfg {Object} baseParams
3805      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3806      */
3807       
3808     /**
3809      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3810      */
3811     timeout: 30,
3812     /**
3813      * @cfg {Sting} align (left|right) for navbar forms
3814      */
3815     align : 'left',
3816
3817     // private
3818     activeAction : null,
3819  
3820     /**
3821      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3822      * element by passing it or its id or mask the form itself by passing in true.
3823      * @type Mixed
3824      */
3825     waitMsgTarget : false,
3826     
3827      
3828     
3829     /**
3830      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3831      * element by passing it or its id or mask the form itself by passing in true.
3832      * @type Mixed
3833      */
3834     
3835     getAutoCreate : function(){
3836         
3837         var cfg = {
3838             tag: 'form',
3839             method : this.method || 'POST',
3840             id : this.id || Roo.id(),
3841             cls : ''
3842         }
3843         if (this.parent().xtype.match(/^Nav/)) {
3844             cfg.cls = 'navbar-form navbar-' + this.align;
3845             
3846         }
3847         
3848         if (this.labelAlign == 'left' ) {
3849             cfg.cls += ' form-horizontal';
3850         }
3851         
3852         
3853         return cfg;
3854     },
3855     initEvents : function()
3856     {
3857         this.el.on('submit', this.onSubmit, this);
3858         
3859         
3860     },
3861     // private
3862     onSubmit : function(e){
3863         e.stopEvent();
3864     },
3865     
3866      /**
3867      * Returns true if client-side validation on the form is successful.
3868      * @return Boolean
3869      */
3870     isValid : function(){
3871         var items = this.getItems();
3872         var valid = true;
3873         items.each(function(f){
3874            if(!f.validate()){
3875                valid = false;
3876                
3877            }
3878         });
3879         return valid;
3880     },
3881     /**
3882      * Returns true if any fields in this form have changed since their original load.
3883      * @return Boolean
3884      */
3885     isDirty : function(){
3886         var dirty = false;
3887         var items = this.getItems();
3888         items.each(function(f){
3889            if(f.isDirty()){
3890                dirty = true;
3891                return false;
3892            }
3893            return true;
3894         });
3895         return dirty;
3896     },
3897      /**
3898      * Performs a predefined action (submit or load) or custom actions you define on this form.
3899      * @param {String} actionName The name of the action type
3900      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3901      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3902      * accept other config options):
3903      * <pre>
3904 Property          Type             Description
3905 ----------------  ---------------  ----------------------------------------------------------------------------------
3906 url               String           The url for the action (defaults to the form's url)
3907 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3908 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3909 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3910                                    validate the form on the client (defaults to false)
3911      * </pre>
3912      * @return {BasicForm} this
3913      */
3914     doAction : function(action, options){
3915         if(typeof action == 'string'){
3916             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3917         }
3918         if(this.fireEvent('beforeaction', this, action) !== false){
3919             this.beforeAction(action);
3920             action.run.defer(100, action);
3921         }
3922         return this;
3923     },
3924     
3925     // private
3926     beforeAction : function(action){
3927         var o = action.options;
3928         
3929         // not really supported yet.. ??
3930         
3931         //if(this.waitMsgTarget === true){
3932             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3933         //}else if(this.waitMsgTarget){
3934         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3935         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3936         //}else {
3937         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3938        // }
3939          
3940     },
3941
3942     // private
3943     afterAction : function(action, success){
3944         this.activeAction = null;
3945         var o = action.options;
3946         
3947         //if(this.waitMsgTarget === true){
3948             this.el.unmask();
3949         //}else if(this.waitMsgTarget){
3950         //    this.waitMsgTarget.unmask();
3951         //}else{
3952         //    Roo.MessageBox.updateProgress(1);
3953         //    Roo.MessageBox.hide();
3954        // }
3955         // 
3956         if(success){
3957             if(o.reset){
3958                 this.reset();
3959             }
3960             Roo.callback(o.success, o.scope, [this, action]);
3961             this.fireEvent('actioncomplete', this, action);
3962             
3963         }else{
3964             
3965             // failure condition..
3966             // we have a scenario where updates need confirming.
3967             // eg. if a locking scenario exists..
3968             // we look for { errors : { needs_confirm : true }} in the response.
3969             if (
3970                 (typeof(action.result) != 'undefined')  &&
3971                 (typeof(action.result.errors) != 'undefined')  &&
3972                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3973            ){
3974                 var _t = this;
3975                 Roo.log("not supported yet");
3976                  /*
3977                 
3978                 Roo.MessageBox.confirm(
3979                     "Change requires confirmation",
3980                     action.result.errorMsg,
3981                     function(r) {
3982                         if (r != 'yes') {
3983                             return;
3984                         }
3985                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
3986                     }
3987                     
3988                 );
3989                 */
3990                 
3991                 
3992                 return;
3993             }
3994             
3995             Roo.callback(o.failure, o.scope, [this, action]);
3996             // show an error message if no failed handler is set..
3997             if (!this.hasListener('actionfailed')) {
3998                 Roo.log("need to add dialog support");
3999                 /*
4000                 Roo.MessageBox.alert("Error",
4001                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4002                         action.result.errorMsg :
4003                         "Saving Failed, please check your entries or try again"
4004                 );
4005                 */
4006             }
4007             
4008             this.fireEvent('actionfailed', this, action);
4009         }
4010         
4011     },
4012     /**
4013      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4014      * @param {String} id The value to search for
4015      * @return Field
4016      */
4017     findField : function(id){
4018         var items = this.getItems();
4019         var field = items.get(id);
4020         if(!field){
4021              items.each(function(f){
4022                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4023                     field = f;
4024                     return false;
4025                 }
4026                 return true;
4027             });
4028         }
4029         return field || null;
4030     },
4031      /**
4032      * Mark fields in this form invalid in bulk.
4033      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4034      * @return {BasicForm} this
4035      */
4036     markInvalid : function(errors){
4037         if(errors instanceof Array){
4038             for(var i = 0, len = errors.length; i < len; i++){
4039                 var fieldError = errors[i];
4040                 var f = this.findField(fieldError.id);
4041                 if(f){
4042                     f.markInvalid(fieldError.msg);
4043                 }
4044             }
4045         }else{
4046             var field, id;
4047             for(id in errors){
4048                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4049                     field.markInvalid(errors[id]);
4050                 }
4051             }
4052         }
4053         //Roo.each(this.childForms || [], function (f) {
4054         //    f.markInvalid(errors);
4055         //});
4056         
4057         return this;
4058     },
4059
4060     /**
4061      * Set values for fields in this form in bulk.
4062      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4063      * @return {BasicForm} this
4064      */
4065     setValues : function(values){
4066         if(values instanceof Array){ // array of objects
4067             for(var i = 0, len = values.length; i < len; i++){
4068                 var v = values[i];
4069                 var f = this.findField(v.id);
4070                 if(f){
4071                     f.setValue(v.value);
4072                     if(this.trackResetOnLoad){
4073                         f.originalValue = f.getValue();
4074                     }
4075                 }
4076             }
4077         }else{ // object hash
4078             var field, id;
4079             for(id in values){
4080                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4081                     
4082                     if (field.setFromData && 
4083                         field.valueField && 
4084                         field.displayField &&
4085                         // combos' with local stores can 
4086                         // be queried via setValue()
4087                         // to set their value..
4088                         (field.store && !field.store.isLocal)
4089                         ) {
4090                         // it's a combo
4091                         var sd = { };
4092                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4093                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4094                         field.setFromData(sd);
4095                         
4096                     } else {
4097                         field.setValue(values[id]);
4098                     }
4099                     
4100                     
4101                     if(this.trackResetOnLoad){
4102                         field.originalValue = field.getValue();
4103                     }
4104                 }
4105             }
4106         }
4107          
4108         //Roo.each(this.childForms || [], function (f) {
4109         //    f.setValues(values);
4110         //});
4111                 
4112         return this;
4113     },
4114
4115     /**
4116      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4117      * they are returned as an array.
4118      * @param {Boolean} asString
4119      * @return {Object}
4120      */
4121     getValues : function(asString){
4122         //if (this.childForms) {
4123             // copy values from the child forms
4124         //    Roo.each(this.childForms, function (f) {
4125         //        this.setValues(f.getValues());
4126         //    }, this);
4127         //}
4128         
4129         
4130         
4131         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4132         if(asString === true){
4133             return fs;
4134         }
4135         return Roo.urlDecode(fs);
4136     },
4137     
4138     /**
4139      * Returns the fields in this form as an object with key/value pairs. 
4140      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4141      * @return {Object}
4142      */
4143     getFieldValues : function(with_hidden)
4144     {
4145         var items = this.getItems();
4146         var ret = {};
4147         items.each(function(f){
4148             if (!f.getName()) {
4149                 return;
4150             }
4151             var v = f.getValue();
4152             if (f.inputType =='radio') {
4153                 if (typeof(ret[f.getName()]) == 'undefined') {
4154                     ret[f.getName()] = ''; // empty..
4155                 }
4156                 
4157                 if (!f.el.dom.checked) {
4158                     return;
4159                     
4160                 }
4161                 v = f.el.dom.value;
4162                 
4163             }
4164             
4165             // not sure if this supported any more..
4166             if ((typeof(v) == 'object') && f.getRawValue) {
4167                 v = f.getRawValue() ; // dates..
4168             }
4169             // combo boxes where name != hiddenName...
4170             if (f.name != f.getName()) {
4171                 ret[f.name] = f.getRawValue();
4172             }
4173             ret[f.getName()] = v;
4174         });
4175         
4176         return ret;
4177     },
4178
4179     /**
4180      * Clears all invalid messages in this form.
4181      * @return {BasicForm} this
4182      */
4183     clearInvalid : function(){
4184         var items = this.getItems();
4185         
4186         items.each(function(f){
4187            f.clearInvalid();
4188         });
4189         
4190         
4191         
4192         return this;
4193     },
4194
4195     /**
4196      * Resets this form.
4197      * @return {BasicForm} this
4198      */
4199     reset : function(){
4200         var items = this.getItems();
4201         items.each(function(f){
4202             f.reset();
4203         });
4204         
4205         Roo.each(this.childForms || [], function (f) {
4206             f.reset();
4207         });
4208        
4209         
4210         return this;
4211     },
4212     getItems : function()
4213     {
4214         var r=new Roo.util.MixedCollection(false, function(o){
4215             return o.id || (o.id = Roo.id());
4216         });
4217         var iter = function(el) {
4218             if (el.inputEl) {
4219                 r.add(el);
4220             }
4221             if (!el.items) {
4222                 return;
4223             }
4224             Roo.each(el.items,function(e) {
4225                 iter(e);
4226             });
4227             
4228             
4229         };
4230         iter(this);
4231         return r;
4232         
4233         
4234         
4235         
4236     }
4237     
4238 });
4239
4240  
4241 /*
4242  * Based on:
4243  * Ext JS Library 1.1.1
4244  * Copyright(c) 2006-2007, Ext JS, LLC.
4245  *
4246  * Originally Released Under LGPL - original licence link has changed is not relivant.
4247  *
4248  * Fork - LGPL
4249  * <script type="text/javascript">
4250  */
4251 /**
4252  * @class Roo.form.VTypes
4253  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4254  * @singleton
4255  */
4256 Roo.form.VTypes = function(){
4257     // closure these in so they are only created once.
4258     var alpha = /^[a-zA-Z_]+$/;
4259     var alphanum = /^[a-zA-Z0-9_]+$/;
4260     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4261     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4262
4263     // All these messages and functions are configurable
4264     return {
4265         /**
4266          * The function used to validate email addresses
4267          * @param {String} value The email address
4268          */
4269         'email' : function(v){
4270             return email.test(v);
4271         },
4272         /**
4273          * The error text to display when the email validation function returns false
4274          * @type String
4275          */
4276         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4277         /**
4278          * The keystroke filter mask to be applied on email input
4279          * @type RegExp
4280          */
4281         'emailMask' : /[a-z0-9_\.\-@]/i,
4282
4283         /**
4284          * The function used to validate URLs
4285          * @param {String} value The URL
4286          */
4287         'url' : function(v){
4288             return url.test(v);
4289         },
4290         /**
4291          * The error text to display when the url validation function returns false
4292          * @type String
4293          */
4294         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4295         
4296         /**
4297          * The function used to validate alpha values
4298          * @param {String} value The value
4299          */
4300         'alpha' : function(v){
4301             return alpha.test(v);
4302         },
4303         /**
4304          * The error text to display when the alpha validation function returns false
4305          * @type String
4306          */
4307         'alphaText' : 'This field should only contain letters and _',
4308         /**
4309          * The keystroke filter mask to be applied on alpha input
4310          * @type RegExp
4311          */
4312         'alphaMask' : /[a-z_]/i,
4313
4314         /**
4315          * The function used to validate alphanumeric values
4316          * @param {String} value The value
4317          */
4318         'alphanum' : function(v){
4319             return alphanum.test(v);
4320         },
4321         /**
4322          * The error text to display when the alphanumeric validation function returns false
4323          * @type String
4324          */
4325         'alphanumText' : 'This field should only contain letters, numbers and _',
4326         /**
4327          * The keystroke filter mask to be applied on alphanumeric input
4328          * @type RegExp
4329          */
4330         'alphanumMask' : /[a-z0-9_]/i
4331     };
4332 }();/*
4333  * - LGPL
4334  *
4335  * Input
4336  * 
4337  */
4338
4339 /**
4340  * @class Roo.bootstrap.Input
4341  * @extends Roo.bootstrap.Component
4342  * Bootstrap Input class
4343  * @cfg {Boolean} disabled is it disabled
4344  * @cfg {String} fieldLabel - the label associated
4345  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4346  * @cfg {String} name name of the input
4347  * @cfg {string} fieldLabel - the label associated
4348  * @cfg {string}  inputType - input / file submit ...
4349  * @cfg {string} placeholder - placeholder to put in text.
4350  * @cfg {string}  before - input group add on before
4351  * @cfg {string} after - input group add on after
4352  * @cfg {string} size - (lg|sm) or leave empty..
4353  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4354  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4355  * @cfg {Number} md colspan out of 12 for computer-sized screens
4356  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4357  * @cfg {string} value default value of the input
4358  * @cfg {Number} labelWidth set the width of label (0-12)
4359  * @cfg {String} labelAlign (top|left)
4360  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4361  * 
4362  * 
4363  * @constructor
4364  * Create a new Input
4365  * @param {Object} config The config object
4366  */
4367
4368 Roo.bootstrap.Input = function(config){
4369     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4370    
4371         this.addEvents({
4372             /**
4373              * @event focus
4374              * Fires when this field receives input focus.
4375              * @param {Roo.form.Field} this
4376              */
4377             focus : true,
4378             /**
4379              * @event blur
4380              * Fires when this field loses input focus.
4381              * @param {Roo.form.Field} this
4382              */
4383             blur : true,
4384             /**
4385              * @event specialkey
4386              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4387              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4388              * @param {Roo.form.Field} this
4389              * @param {Roo.EventObject} e The event object
4390              */
4391             specialkey : true,
4392             /**
4393              * @event change
4394              * Fires just before the field blurs if the field value has changed.
4395              * @param {Roo.form.Field} this
4396              * @param {Mixed} newValue The new value
4397              * @param {Mixed} oldValue The original value
4398              */
4399             change : true,
4400             /**
4401              * @event invalid
4402              * Fires after the field has been marked as invalid.
4403              * @param {Roo.form.Field} this
4404              * @param {String} msg The validation message
4405              */
4406             invalid : true,
4407             /**
4408              * @event valid
4409              * Fires after the field has been validated with no errors.
4410              * @param {Roo.form.Field} this
4411              */
4412             valid : true,
4413              /**
4414              * @event keyup
4415              * Fires after the key up
4416              * @param {Roo.form.Field} this
4417              * @param {Roo.EventObject}  e The event Object
4418              */
4419             keyup : true
4420         });
4421 };
4422
4423 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4424      /**
4425      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4426       automatic validation (defaults to "keyup").
4427      */
4428     validationEvent : "keyup",
4429      /**
4430      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4431      */
4432     validateOnBlur : true,
4433     /**
4434      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4435      */
4436     validationDelay : 250,
4437      /**
4438      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4439      */
4440     focusClass : "x-form-focus",  // not needed???
4441     
4442        
4443     /**
4444      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4445      */
4446     invalidClass : "has-error",
4447     
4448     /**
4449      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4450      */
4451     selectOnFocus : false,
4452     
4453      /**
4454      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4455      */
4456     maskRe : null,
4457        /**
4458      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4459      */
4460     vtype : null,
4461     
4462       /**
4463      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4464      */
4465     disableKeyFilter : false,
4466     
4467        /**
4468      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4469      */
4470     disabled : false,
4471      /**
4472      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4473      */
4474     allowBlank : true,
4475     /**
4476      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4477      */
4478     blankText : "This field is required",
4479     
4480      /**
4481      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4482      */
4483     minLength : 0,
4484     /**
4485      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4486      */
4487     maxLength : Number.MAX_VALUE,
4488     /**
4489      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4490      */
4491     minLengthText : "The minimum length for this field is {0}",
4492     /**
4493      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4494      */
4495     maxLengthText : "The maximum length for this field is {0}",
4496   
4497     
4498     /**
4499      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4500      * If available, this function will be called only after the basic validators all return true, and will be passed the
4501      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4502      */
4503     validator : null,
4504     /**
4505      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4506      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4507      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4508      */
4509     regex : null,
4510     /**
4511      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4512      */
4513     regexText : "",
4514     
4515     
4516     
4517     fieldLabel : '',
4518     inputType : 'text',
4519     
4520     name : false,
4521     placeholder: false,
4522     before : false,
4523     after : false,
4524     size : false,
4525     // private
4526     hasFocus : false,
4527     preventMark: false,
4528     isFormField : true,
4529     value : '',
4530     labelWidth : 2,
4531     labelAlign : false,
4532     readOnly : false,
4533     
4534     parentLabelAlign : function()
4535     {
4536         var parent = this;
4537         while (parent.parent()) {
4538             parent = parent.parent();
4539             if (typeof(parent.labelAlign) !='undefined') {
4540                 return parent.labelAlign;
4541             }
4542         }
4543         return 'left';
4544         
4545     },
4546     
4547     getAutoCreate : function(){
4548         
4549         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4550         
4551         var id = Roo.id();
4552         
4553         var cfg = {};
4554         
4555         if(this.inputType != 'hidden'){
4556             cfg.cls = 'form-group' //input-group
4557         }
4558         
4559         var input =  {
4560             tag: 'input',
4561             id : id,
4562             type : this.inputType,
4563             value : this.value,
4564             cls : 'form-control',
4565             placeholder : this.placeholder || ''
4566             
4567         };
4568         
4569         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4570             input.maxLength = this.maxLength;
4571         }
4572         
4573         if (this.disabled) {
4574             input.disabled=true;
4575         }
4576         
4577         if (this.readOnly) {
4578             input.readonly=true;
4579         }
4580         
4581         if (this.name) {
4582             input.name = this.name;
4583         }
4584         if (this.size) {
4585             input.cls += ' input-' + this.size;
4586         }
4587         var settings=this;
4588         ['xs','sm','md','lg'].map(function(size){
4589             if (settings[size]) {
4590                 cfg.cls += ' col-' + size + '-' + settings[size];
4591             }
4592         });
4593         
4594         var inputblock = input;
4595         
4596         if (this.before || this.after) {
4597             
4598             inputblock = {
4599                 cls : 'input-group',
4600                 cn :  [] 
4601             };
4602             if (this.before) {
4603                 inputblock.cn.push({
4604                     tag :'span',
4605                     cls : 'input-group-addon',
4606                     html : this.before
4607                 });
4608             }
4609             inputblock.cn.push(input);
4610             if (this.after) {
4611                 inputblock.cn.push({
4612                     tag :'span',
4613                     cls : 'input-group-addon',
4614                     html : this.after
4615                 });
4616             }
4617             
4618         };
4619         
4620         if (align ==='left' && this.fieldLabel.length) {
4621                 Roo.log("left and has label");
4622                 cfg.cn = [
4623                     
4624                     {
4625                         tag: 'label',
4626                         'for' :  id,
4627                         cls : 'control-label col-sm-' + this.labelWidth,
4628                         html : this.fieldLabel
4629                         
4630                     },
4631                     {
4632                         cls : "col-sm-" + (12 - this.labelWidth), 
4633                         cn: [
4634                             inputblock
4635                         ]
4636                     }
4637                     
4638                 ];
4639         } else if ( this.fieldLabel.length) {
4640                 Roo.log(" label");
4641                  cfg.cn = [
4642                    
4643                     {
4644                         tag: 'label',
4645                         //cls : 'input-group-addon',
4646                         html : this.fieldLabel
4647                         
4648                     },
4649                     
4650                     inputblock
4651                     
4652                 ];
4653
4654         } else {
4655             
4656                 Roo.log(" no label && no align");
4657                 cfg.cn = [
4658                     
4659                         inputblock
4660                     
4661                 ];
4662                 
4663                 
4664         };
4665         Roo.log('input-parentType: ' + this.parentType);
4666         
4667         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4668            cfg.cls += ' navbar-form';
4669            Roo.log(cfg);
4670         }
4671         
4672         return cfg;
4673         
4674     },
4675     /**
4676      * return the real input element.
4677      */
4678     inputEl: function ()
4679     {
4680         return this.el.select('input.form-control',true).first();
4681     },
4682     setDisabled : function(v)
4683     {
4684         var i  = this.inputEl().dom;
4685         if (!v) {
4686             i.removeAttribute('disabled');
4687             return;
4688             
4689         }
4690         i.setAttribute('disabled','true');
4691     },
4692     initEvents : function()
4693     {
4694         
4695         this.inputEl().on("keydown" , this.fireKey,  this);
4696         this.inputEl().on("focus", this.onFocus,  this);
4697         this.inputEl().on("blur", this.onBlur,  this);
4698         
4699         this.inputEl().relayEvent('keyup', this);
4700
4701         // reference to original value for reset
4702         this.originalValue = this.getValue();
4703         //Roo.form.TextField.superclass.initEvents.call(this);
4704         if(this.validationEvent == 'keyup'){
4705             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4706             this.inputEl().on('keyup', this.filterValidation, this);
4707         }
4708         else if(this.validationEvent !== false){
4709             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4710         }
4711         
4712         if(this.selectOnFocus){
4713             this.on("focus", this.preFocus, this);
4714             
4715         }
4716         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4717             this.inputEl().on("keypress", this.filterKeys, this);
4718         }
4719        /* if(this.grow){
4720             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4721             this.el.on("click", this.autoSize,  this);
4722         }
4723         */
4724         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4725             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4726         }
4727         
4728     },
4729     filterValidation : function(e){
4730         if(!e.isNavKeyPress()){
4731             this.validationTask.delay(this.validationDelay);
4732         }
4733     },
4734      /**
4735      * Validates the field value
4736      * @return {Boolean} True if the value is valid, else false
4737      */
4738     validate : function(){
4739         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4740         if(this.disabled || this.validateValue(this.getRawValue())){
4741             this.clearInvalid();
4742             return true;
4743         }
4744         return false;
4745     },
4746     
4747     
4748     /**
4749      * Validates a value according to the field's validation rules and marks the field as invalid
4750      * if the validation fails
4751      * @param {Mixed} value The value to validate
4752      * @return {Boolean} True if the value is valid, else false
4753      */
4754     validateValue : function(value){
4755         if(value.length < 1)  { // if it's blank
4756              if(this.allowBlank){
4757                 this.clearInvalid();
4758                 return true;
4759              }else{
4760                 this.markInvalid(this.blankText);
4761                 return false;
4762              }
4763         }
4764         if(value.length < this.minLength){
4765             this.markInvalid(String.format(this.minLengthText, this.minLength));
4766             return false;
4767         }
4768         if(value.length > this.maxLength){
4769             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4770             return false;
4771         }
4772         if(this.vtype){
4773             var vt = Roo.form.VTypes;
4774             if(!vt[this.vtype](value, this)){
4775                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4776                 return false;
4777             }
4778         }
4779         if(typeof this.validator == "function"){
4780             var msg = this.validator(value);
4781             if(msg !== true){
4782                 this.markInvalid(msg);
4783                 return false;
4784             }
4785         }
4786         if(this.regex && !this.regex.test(value)){
4787             this.markInvalid(this.regexText);
4788             return false;
4789         }
4790         return true;
4791     },
4792
4793     
4794     
4795      // private
4796     fireKey : function(e){
4797         //Roo.log('field ' + e.getKey());
4798         if(e.isNavKeyPress()){
4799             this.fireEvent("specialkey", this, e);
4800         }
4801     },
4802     focus : function (selectText){
4803         if(this.rendered){
4804             this.inputEl().focus();
4805             if(selectText === true){
4806                 this.inputEl().dom.select();
4807             }
4808         }
4809         return this;
4810     } ,
4811     
4812     onFocus : function(){
4813         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4814            // this.el.addClass(this.focusClass);
4815         }
4816         if(!this.hasFocus){
4817             this.hasFocus = true;
4818             this.startValue = this.getValue();
4819             this.fireEvent("focus", this);
4820         }
4821     },
4822     
4823     beforeBlur : Roo.emptyFn,
4824
4825     
4826     // private
4827     onBlur : function(){
4828         this.beforeBlur();
4829         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4830             //this.el.removeClass(this.focusClass);
4831         }
4832         this.hasFocus = false;
4833         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4834             this.validate();
4835         }
4836         var v = this.getValue();
4837         if(String(v) !== String(this.startValue)){
4838             this.fireEvent('change', this, v, this.startValue);
4839         }
4840         this.fireEvent("blur", this);
4841     },
4842     
4843     /**
4844      * Resets the current field value to the originally loaded value and clears any validation messages
4845      */
4846     reset : function(){
4847         this.setValue(this.originalValue);
4848         this.clearInvalid();
4849     },
4850      /**
4851      * Returns the name of the field
4852      * @return {Mixed} name The name field
4853      */
4854     getName: function(){
4855         return this.name;
4856     },
4857      /**
4858      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4859      * @return {Mixed} value The field value
4860      */
4861     getValue : function(){
4862         return this.inputEl().getValue();
4863     },
4864     /**
4865      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4866      * @return {Mixed} value The field value
4867      */
4868     getRawValue : function(){
4869         var v = this.inputEl().getValue();
4870         
4871         return v;
4872     },
4873     
4874     /**
4875      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4876      * @param {Mixed} value The value to set
4877      */
4878     setRawValue : function(v){
4879         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4880     },
4881     
4882     selectText : function(start, end){
4883         var v = this.getRawValue();
4884         if(v.length > 0){
4885             start = start === undefined ? 0 : start;
4886             end = end === undefined ? v.length : end;
4887             var d = this.inputEl().dom;
4888             if(d.setSelectionRange){
4889                 d.setSelectionRange(start, end);
4890             }else if(d.createTextRange){
4891                 var range = d.createTextRange();
4892                 range.moveStart("character", start);
4893                 range.moveEnd("character", v.length-end);
4894                 range.select();
4895             }
4896         }
4897     },
4898     
4899     /**
4900      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4901      * @param {Mixed} value The value to set
4902      */
4903     setValue : function(v){
4904         this.value = v;
4905         if(this.rendered){
4906             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4907             this.validate();
4908         }
4909     },
4910     
4911     /*
4912     processValue : function(value){
4913         if(this.stripCharsRe){
4914             var newValue = value.replace(this.stripCharsRe, '');
4915             if(newValue !== value){
4916                 this.setRawValue(newValue);
4917                 return newValue;
4918             }
4919         }
4920         return value;
4921     },
4922   */
4923     preFocus : function(){
4924         
4925         if(this.selectOnFocus){
4926             this.inputEl().dom.select();
4927         }
4928     },
4929     filterKeys : function(e){
4930         var k = e.getKey();
4931         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4932             return;
4933         }
4934         var c = e.getCharCode(), cc = String.fromCharCode(c);
4935         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4936             return;
4937         }
4938         if(!this.maskRe.test(cc)){
4939             e.stopEvent();
4940         }
4941     },
4942      /**
4943      * Clear any invalid styles/messages for this field
4944      */
4945     clearInvalid : function(){
4946         
4947         if(!this.el || this.preventMark){ // not rendered
4948             return;
4949         }
4950         this.el.removeClass(this.invalidClass);
4951         /*
4952         switch(this.msgTarget){
4953             case 'qtip':
4954                 this.el.dom.qtip = '';
4955                 break;
4956             case 'title':
4957                 this.el.dom.title = '';
4958                 break;
4959             case 'under':
4960                 if(this.errorEl){
4961                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4962                 }
4963                 break;
4964             case 'side':
4965                 if(this.errorIcon){
4966                     this.errorIcon.dom.qtip = '';
4967                     this.errorIcon.hide();
4968                     this.un('resize', this.alignErrorIcon, this);
4969                 }
4970                 break;
4971             default:
4972                 var t = Roo.getDom(this.msgTarget);
4973                 t.innerHTML = '';
4974                 t.style.display = 'none';
4975                 break;
4976         }
4977         */
4978         this.fireEvent('valid', this);
4979     },
4980      /**
4981      * Mark this field as invalid
4982      * @param {String} msg The validation message
4983      */
4984     markInvalid : function(msg){
4985         if(!this.el  || this.preventMark){ // not rendered
4986             return;
4987         }
4988         this.el.addClass(this.invalidClass);
4989         /*
4990         msg = msg || this.invalidText;
4991         switch(this.msgTarget){
4992             case 'qtip':
4993                 this.el.dom.qtip = msg;
4994                 this.el.dom.qclass = 'x-form-invalid-tip';
4995                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
4996                     Roo.QuickTips.enable();
4997                 }
4998                 break;
4999             case 'title':
5000                 this.el.dom.title = msg;
5001                 break;
5002             case 'under':
5003                 if(!this.errorEl){
5004                     var elp = this.el.findParent('.x-form-element', 5, true);
5005                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5006                     this.errorEl.setWidth(elp.getWidth(true)-20);
5007                 }
5008                 this.errorEl.update(msg);
5009                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5010                 break;
5011             case 'side':
5012                 if(!this.errorIcon){
5013                     var elp = this.el.findParent('.x-form-element', 5, true);
5014                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5015                 }
5016                 this.alignErrorIcon();
5017                 this.errorIcon.dom.qtip = msg;
5018                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5019                 this.errorIcon.show();
5020                 this.on('resize', this.alignErrorIcon, this);
5021                 break;
5022             default:
5023                 var t = Roo.getDom(this.msgTarget);
5024                 t.innerHTML = msg;
5025                 t.style.display = this.msgDisplay;
5026                 break;
5027         }
5028         */
5029         this.fireEvent('invalid', this, msg);
5030     },
5031     // private
5032     SafariOnKeyDown : function(event)
5033     {
5034         // this is a workaround for a password hang bug on chrome/ webkit.
5035         
5036         var isSelectAll = false;
5037         
5038         if(this.inputEl().dom.selectionEnd > 0){
5039             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5040         }
5041         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5042             event.preventDefault();
5043             this.setValue('');
5044             return;
5045         }
5046         
5047         if(isSelectAll){ // backspace and delete key
5048             
5049             event.preventDefault();
5050             // this is very hacky as keydown always get's upper case.
5051             //
5052             var cc = String.fromCharCode(event.getCharCode());
5053             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5054             
5055         }
5056     },
5057     adjustWidth : function(tag, w){
5058         tag = tag.toLowerCase();
5059         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5060             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5061                 if(tag == 'input'){
5062                     return w + 2;
5063                 }
5064                 if(tag == 'textarea'){
5065                     return w-2;
5066                 }
5067             }else if(Roo.isOpera){
5068                 if(tag == 'input'){
5069                     return w + 2;
5070                 }
5071                 if(tag == 'textarea'){
5072                     return w-2;
5073                 }
5074             }
5075         }
5076         return w;
5077     }
5078     
5079 });
5080
5081  
5082 /*
5083  * - LGPL
5084  *
5085  * Input
5086  * 
5087  */
5088
5089 /**
5090  * @class Roo.bootstrap.TextArea
5091  * @extends Roo.bootstrap.Input
5092  * Bootstrap TextArea class
5093  * @cfg {Number} cols Specifies the visible width of a text area
5094  * @cfg {Number} rows Specifies the visible number of lines in a text area
5095  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5096  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5097  * @cfg {string} html text
5098  * 
5099  * @constructor
5100  * Create a new TextArea
5101  * @param {Object} config The config object
5102  */
5103
5104 Roo.bootstrap.TextArea = function(config){
5105     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5106    
5107 };
5108
5109 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5110      
5111     cols : false,
5112     rows : 5,
5113     readOnly : false,
5114     warp : 'soft',
5115     resize : false,
5116     value: false,
5117     html: false,
5118     
5119     getAutoCreate : function(){
5120         
5121         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5122         
5123         var id = Roo.id();
5124         
5125         var cfg = {};
5126         
5127         var input =  {
5128             tag: 'textarea',
5129             id : id,
5130             warp : this.warp,
5131             rows : this.rows,
5132             value : this.value || '',
5133             html: this.html || '',
5134             cls : 'form-control',
5135             placeholder : this.placeholder || '' 
5136             
5137         };
5138         
5139         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5140             input.maxLength = this.maxLength;
5141         }
5142         
5143         if(this.resize){
5144             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5145         }
5146         
5147         if(this.cols){
5148             input.cols = this.cols;
5149         }
5150         
5151         if (this.readOnly) {
5152             input.readonly = true;
5153         }
5154         
5155         if (this.name) {
5156             input.name = this.name;
5157         }
5158         
5159         if (this.size) {
5160             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5161         }
5162         
5163         var settings=this;
5164         ['xs','sm','md','lg'].map(function(size){
5165             if (settings[size]) {
5166                 cfg.cls += ' col-' + size + '-' + settings[size];
5167             }
5168         });
5169         
5170         var inputblock = input;
5171         
5172         if (this.before || this.after) {
5173             
5174             inputblock = {
5175                 cls : 'input-group',
5176                 cn :  [] 
5177             };
5178             if (this.before) {
5179                 inputblock.cn.push({
5180                     tag :'span',
5181                     cls : 'input-group-addon',
5182                     html : this.before
5183                 });
5184             }
5185             inputblock.cn.push(input);
5186             if (this.after) {
5187                 inputblock.cn.push({
5188                     tag :'span',
5189                     cls : 'input-group-addon',
5190                     html : this.after
5191                 });
5192             }
5193             
5194         }
5195         
5196         if (align ==='left' && this.fieldLabel.length) {
5197                 Roo.log("left and has label");
5198                 cfg.cn = [
5199                     
5200                     {
5201                         tag: 'label',
5202                         'for' :  id,
5203                         cls : 'control-label col-sm-' + this.labelWidth,
5204                         html : this.fieldLabel
5205                         
5206                     },
5207                     {
5208                         cls : "col-sm-" + (12 - this.labelWidth), 
5209                         cn: [
5210                             inputblock
5211                         ]
5212                     }
5213                     
5214                 ];
5215         } else if ( this.fieldLabel.length) {
5216                 Roo.log(" label");
5217                  cfg.cn = [
5218                    
5219                     {
5220                         tag: 'label',
5221                         //cls : 'input-group-addon',
5222                         html : this.fieldLabel
5223                         
5224                     },
5225                     
5226                     inputblock
5227                     
5228                 ];
5229
5230         } else {
5231             
5232                    Roo.log(" no label && no align");
5233                 cfg.cn = [
5234                     
5235                         inputblock
5236                     
5237                 ];
5238                 
5239                 
5240         }
5241         
5242         if (this.disabled) {
5243             input.disabled=true;
5244         }
5245         
5246         return cfg;
5247         
5248     },
5249     /**
5250      * return the real textarea element.
5251      */
5252     inputEl: function ()
5253     {
5254         return this.el.select('textarea.form-control',true).first();
5255     }
5256 });
5257
5258  
5259 /*
5260  * - LGPL
5261  *
5262  * trigger field - base class for combo..
5263  * 
5264  */
5265  
5266 /**
5267  * @class Roo.bootstrap.TriggerField
5268  * @extends Roo.bootstrap.Input
5269  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5270  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5271  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5272  * for which you can provide a custom implementation.  For example:
5273  * <pre><code>
5274 var trigger = new Roo.bootstrap.TriggerField();
5275 trigger.onTriggerClick = myTriggerFn;
5276 trigger.applyTo('my-field');
5277 </code></pre>
5278  *
5279  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5280  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5281  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5282  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5283  * @constructor
5284  * Create a new TriggerField.
5285  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5286  * to the base TextField)
5287  */
5288 Roo.bootstrap.TriggerField = function(config){
5289     this.mimicing = false;
5290     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5291 };
5292
5293 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5294     /**
5295      * @cfg {String} triggerClass A CSS class to apply to the trigger
5296      */
5297      /**
5298      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5299      */
5300     hideTrigger:false,
5301
5302     /** @cfg {Boolean} grow @hide */
5303     /** @cfg {Number} growMin @hide */
5304     /** @cfg {Number} growMax @hide */
5305
5306     /**
5307      * @hide 
5308      * @method
5309      */
5310     autoSize: Roo.emptyFn,
5311     // private
5312     monitorTab : true,
5313     // private
5314     deferHeight : true,
5315
5316     
5317     actionMode : 'wrap',
5318     
5319     
5320     
5321     getAutoCreate : function(){
5322        
5323         var parent = this.parent();
5324         
5325         var align = this.parentLabelAlign();
5326         
5327         var id = Roo.id();
5328         
5329         var cfg = {
5330             cls: 'form-group' //input-group
5331         };
5332         
5333         
5334         var input =  {
5335             tag: 'input',
5336             id : id,
5337             type : this.inputType,
5338             cls : 'form-control',
5339             autocomplete: 'off',
5340             placeholder : this.placeholder || '' 
5341             
5342         };
5343         if (this.name) {
5344             input.name = this.name;
5345         }
5346         if (this.size) {
5347             input.cls += ' input-' + this.size;
5348         }
5349         
5350         if (this.disabled) {
5351             input.disabled=true;
5352         }
5353         
5354         var inputblock = input;
5355         
5356         if (this.before || this.after) {
5357             
5358             inputblock = {
5359                 cls : 'input-group',
5360                 cn :  [] 
5361             };
5362             if (this.before) {
5363                 inputblock.cn.push({
5364                     tag :'span',
5365                     cls : 'input-group-addon',
5366                     html : this.before
5367                 });
5368             }
5369             inputblock.cn.push(input);
5370             if (this.after) {
5371                 inputblock.cn.push({
5372                     tag :'span',
5373                     cls : 'input-group-addon',
5374                     html : this.after
5375                 });
5376             }
5377             
5378         };
5379         
5380         var box = {
5381             tag: 'div',
5382             cn: [
5383                 {
5384                     tag: 'input',
5385                     type : 'hidden',
5386                     cls: 'form-hidden-field'
5387                 },
5388                 inputblock
5389             ]
5390             
5391         };
5392         
5393         if(this.multiple){
5394             Roo.log('multiple');
5395             
5396             box = {
5397                 tag: 'div',
5398                 cn: [
5399                     {
5400                         tag: 'input',
5401                         type : 'hidden',
5402                         cls: 'form-hidden-field'
5403                     },
5404                     {
5405                         tag: 'ul',
5406                         cls: 'select2-choices',
5407                         cn:[
5408                             {
5409                                 tag: 'li',
5410                                 cls: 'select2-search-field',
5411                                 cn: [
5412
5413                                     inputblock
5414                                 ]
5415                             }
5416                         ]
5417                     }
5418                 ]
5419             }
5420         };
5421         
5422         var combobox = {
5423             cls: 'select2-container input-group',
5424             cn: [
5425                 box,
5426                 {
5427                     tag: 'ul',
5428                     cls: 'typeahead typeahead-long dropdown-menu',
5429                     style: 'display:none'
5430                 }
5431             ]
5432         };
5433         
5434         if(!this.multiple){
5435             combobox.cn.push({
5436                 tag :'span',
5437                 cls : 'input-group-addon btn dropdown-toggle',
5438                 cn : [
5439                     {
5440                         tag: 'span',
5441                         cls: 'caret'
5442                     },
5443                     {
5444                         tag: 'span',
5445                         cls: 'combobox-clear',
5446                         cn  : [
5447                             {
5448                                 tag : 'i',
5449                                 cls: 'icon-remove'
5450                             }
5451                         ]
5452                     }
5453                 ]
5454
5455             })
5456         }
5457         
5458         if(this.multiple){
5459             combobox.cls += ' select2-container-multi';
5460         }
5461         
5462         if (align ==='left' && this.fieldLabel.length) {
5463             
5464                 Roo.log("left and has label");
5465                 cfg.cn = [
5466                     
5467                     {
5468                         tag: 'label',
5469                         'for' :  id,
5470                         cls : 'control-label col-sm-' + this.labelWidth,
5471                         html : this.fieldLabel
5472                         
5473                     },
5474                     {
5475                         cls : "col-sm-" + (12 - this.labelWidth), 
5476                         cn: [
5477                             combobox
5478                         ]
5479                     }
5480                     
5481                 ];
5482         } else if ( this.fieldLabel.length) {
5483                 Roo.log(" label");
5484                  cfg.cn = [
5485                    
5486                     {
5487                         tag: 'label',
5488                         //cls : 'input-group-addon',
5489                         html : this.fieldLabel
5490                         
5491                     },
5492                     
5493                     combobox
5494                     
5495                 ];
5496
5497         } else {
5498             
5499                 Roo.log(" no label && no align");
5500                 cfg = combobox
5501                      
5502                 
5503         }
5504          
5505         var settings=this;
5506         ['xs','sm','md','lg'].map(function(size){
5507             if (settings[size]) {
5508                 cfg.cls += ' col-' + size + '-' + settings[size];
5509             }
5510         });
5511         
5512         return cfg;
5513         
5514     },
5515     
5516     
5517     
5518     // private
5519     onResize : function(w, h){
5520 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5521 //        if(typeof w == 'number'){
5522 //            var x = w - this.trigger.getWidth();
5523 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5524 //            this.trigger.setStyle('left', x+'px');
5525 //        }
5526     },
5527
5528     // private
5529     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5530
5531     // private
5532     getResizeEl : function(){
5533         return this.inputEl();
5534     },
5535
5536     // private
5537     getPositionEl : function(){
5538         return this.inputEl();
5539     },
5540
5541     // private
5542     alignErrorIcon : function(){
5543         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5544     },
5545
5546     // private
5547     initEvents : function(){
5548         
5549         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5550         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5551         if(!this.multiple){
5552             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5553             if(this.hideTrigger){
5554                 this.trigger.setDisplayed(false);
5555             }
5556             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5557         }
5558         
5559         if(this.multiple){
5560             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5561         }
5562         
5563         //this.trigger.addClassOnOver('x-form-trigger-over');
5564         //this.trigger.addClassOnClick('x-form-trigger-click');
5565         
5566         //if(!this.width){
5567         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5568         //}
5569     },
5570
5571     // private
5572     initTrigger : function(){
5573        
5574     },
5575
5576     // private
5577     onDestroy : function(){
5578         if(this.trigger){
5579             this.trigger.removeAllListeners();
5580           //  this.trigger.remove();
5581         }
5582         //if(this.wrap){
5583         //    this.wrap.remove();
5584         //}
5585         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5586     },
5587
5588     // private
5589     onFocus : function(){
5590         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5591         /*
5592         if(!this.mimicing){
5593             this.wrap.addClass('x-trigger-wrap-focus');
5594             this.mimicing = true;
5595             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5596             if(this.monitorTab){
5597                 this.el.on("keydown", this.checkTab, this);
5598             }
5599         }
5600         */
5601     },
5602
5603     // private
5604     checkTab : function(e){
5605         if(e.getKey() == e.TAB){
5606             this.triggerBlur();
5607         }
5608     },
5609
5610     // private
5611     onBlur : function(){
5612         // do nothing
5613     },
5614
5615     // private
5616     mimicBlur : function(e, t){
5617         /*
5618         if(!this.wrap.contains(t) && this.validateBlur()){
5619             this.triggerBlur();
5620         }
5621         */
5622     },
5623
5624     // private
5625     triggerBlur : function(){
5626         this.mimicing = false;
5627         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5628         if(this.monitorTab){
5629             this.el.un("keydown", this.checkTab, this);
5630         }
5631         //this.wrap.removeClass('x-trigger-wrap-focus');
5632         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5633     },
5634
5635     // private
5636     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5637     validateBlur : function(e, t){
5638         return true;
5639     },
5640
5641     // private
5642     onDisable : function(){
5643         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5644         //if(this.wrap){
5645         //    this.wrap.addClass('x-item-disabled');
5646         //}
5647     },
5648
5649     // private
5650     onEnable : function(){
5651         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5652         //if(this.wrap){
5653         //    this.el.removeClass('x-item-disabled');
5654         //}
5655     },
5656
5657     // private
5658     onShow : function(){
5659         var ae = this.getActionEl();
5660         
5661         if(ae){
5662             ae.dom.style.display = '';
5663             ae.dom.style.visibility = 'visible';
5664         }
5665     },
5666
5667     // private
5668     
5669     onHide : function(){
5670         var ae = this.getActionEl();
5671         ae.dom.style.display = 'none';
5672     },
5673
5674     /**
5675      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5676      * by an implementing function.
5677      * @method
5678      * @param {EventObject} e
5679      */
5680     onTriggerClick : Roo.emptyFn
5681 });
5682  /*
5683  * Based on:
5684  * Ext JS Library 1.1.1
5685  * Copyright(c) 2006-2007, Ext JS, LLC.
5686  *
5687  * Originally Released Under LGPL - original licence link has changed is not relivant.
5688  *
5689  * Fork - LGPL
5690  * <script type="text/javascript">
5691  */
5692
5693
5694 /**
5695  * @class Roo.data.SortTypes
5696  * @singleton
5697  * Defines the default sorting (casting?) comparison functions used when sorting data.
5698  */
5699 Roo.data.SortTypes = {
5700     /**
5701      * Default sort that does nothing
5702      * @param {Mixed} s The value being converted
5703      * @return {Mixed} The comparison value
5704      */
5705     none : function(s){
5706         return s;
5707     },
5708     
5709     /**
5710      * The regular expression used to strip tags
5711      * @type {RegExp}
5712      * @property
5713      */
5714     stripTagsRE : /<\/?[^>]+>/gi,
5715     
5716     /**
5717      * Strips all HTML tags to sort on text only
5718      * @param {Mixed} s The value being converted
5719      * @return {String} The comparison value
5720      */
5721     asText : function(s){
5722         return String(s).replace(this.stripTagsRE, "");
5723     },
5724     
5725     /**
5726      * Strips all HTML tags to sort on text only - Case insensitive
5727      * @param {Mixed} s The value being converted
5728      * @return {String} The comparison value
5729      */
5730     asUCText : function(s){
5731         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5732     },
5733     
5734     /**
5735      * Case insensitive string
5736      * @param {Mixed} s The value being converted
5737      * @return {String} The comparison value
5738      */
5739     asUCString : function(s) {
5740         return String(s).toUpperCase();
5741     },
5742     
5743     /**
5744      * Date sorting
5745      * @param {Mixed} s The value being converted
5746      * @return {Number} The comparison value
5747      */
5748     asDate : function(s) {
5749         if(!s){
5750             return 0;
5751         }
5752         if(s instanceof Date){
5753             return s.getTime();
5754         }
5755         return Date.parse(String(s));
5756     },
5757     
5758     /**
5759      * Float sorting
5760      * @param {Mixed} s The value being converted
5761      * @return {Float} The comparison value
5762      */
5763     asFloat : function(s) {
5764         var val = parseFloat(String(s).replace(/,/g, ""));
5765         if(isNaN(val)) val = 0;
5766         return val;
5767     },
5768     
5769     /**
5770      * Integer sorting
5771      * @param {Mixed} s The value being converted
5772      * @return {Number} The comparison value
5773      */
5774     asInt : function(s) {
5775         var val = parseInt(String(s).replace(/,/g, ""));
5776         if(isNaN(val)) val = 0;
5777         return val;
5778     }
5779 };/*
5780  * Based on:
5781  * Ext JS Library 1.1.1
5782  * Copyright(c) 2006-2007, Ext JS, LLC.
5783  *
5784  * Originally Released Under LGPL - original licence link has changed is not relivant.
5785  *
5786  * Fork - LGPL
5787  * <script type="text/javascript">
5788  */
5789
5790 /**
5791 * @class Roo.data.Record
5792  * Instances of this class encapsulate both record <em>definition</em> information, and record
5793  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5794  * to access Records cached in an {@link Roo.data.Store} object.<br>
5795  * <p>
5796  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5797  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5798  * objects.<br>
5799  * <p>
5800  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5801  * @constructor
5802  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5803  * {@link #create}. The parameters are the same.
5804  * @param {Array} data An associative Array of data values keyed by the field name.
5805  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5806  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5807  * not specified an integer id is generated.
5808  */
5809 Roo.data.Record = function(data, id){
5810     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5811     this.data = data;
5812 };
5813
5814 /**
5815  * Generate a constructor for a specific record layout.
5816  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5817  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5818  * Each field definition object may contain the following properties: <ul>
5819  * <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,
5820  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5821  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5822  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5823  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5824  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5825  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5826  * this may be omitted.</p></li>
5827  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5828  * <ul><li>auto (Default, implies no conversion)</li>
5829  * <li>string</li>
5830  * <li>int</li>
5831  * <li>float</li>
5832  * <li>boolean</li>
5833  * <li>date</li></ul></p></li>
5834  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5835  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5836  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5837  * by the Reader into an object that will be stored in the Record. It is passed the
5838  * following parameters:<ul>
5839  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5840  * </ul></p></li>
5841  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5842  * </ul>
5843  * <br>usage:<br><pre><code>
5844 var TopicRecord = Roo.data.Record.create(
5845     {name: 'title', mapping: 'topic_title'},
5846     {name: 'author', mapping: 'username'},
5847     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5848     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5849     {name: 'lastPoster', mapping: 'user2'},
5850     {name: 'excerpt', mapping: 'post_text'}
5851 );
5852
5853 var myNewRecord = new TopicRecord({
5854     title: 'Do my job please',
5855     author: 'noobie',
5856     totalPosts: 1,
5857     lastPost: new Date(),
5858     lastPoster: 'Animal',
5859     excerpt: 'No way dude!'
5860 });
5861 myStore.add(myNewRecord);
5862 </code></pre>
5863  * @method create
5864  * @static
5865  */
5866 Roo.data.Record.create = function(o){
5867     var f = function(){
5868         f.superclass.constructor.apply(this, arguments);
5869     };
5870     Roo.extend(f, Roo.data.Record);
5871     var p = f.prototype;
5872     p.fields = new Roo.util.MixedCollection(false, function(field){
5873         return field.name;
5874     });
5875     for(var i = 0, len = o.length; i < len; i++){
5876         p.fields.add(new Roo.data.Field(o[i]));
5877     }
5878     f.getField = function(name){
5879         return p.fields.get(name);  
5880     };
5881     return f;
5882 };
5883
5884 Roo.data.Record.AUTO_ID = 1000;
5885 Roo.data.Record.EDIT = 'edit';
5886 Roo.data.Record.REJECT = 'reject';
5887 Roo.data.Record.COMMIT = 'commit';
5888
5889 Roo.data.Record.prototype = {
5890     /**
5891      * Readonly flag - true if this record has been modified.
5892      * @type Boolean
5893      */
5894     dirty : false,
5895     editing : false,
5896     error: null,
5897     modified: null,
5898
5899     // private
5900     join : function(store){
5901         this.store = store;
5902     },
5903
5904     /**
5905      * Set the named field to the specified value.
5906      * @param {String} name The name of the field to set.
5907      * @param {Object} value The value to set the field to.
5908      */
5909     set : function(name, value){
5910         if(this.data[name] == value){
5911             return;
5912         }
5913         this.dirty = true;
5914         if(!this.modified){
5915             this.modified = {};
5916         }
5917         if(typeof this.modified[name] == 'undefined'){
5918             this.modified[name] = this.data[name];
5919         }
5920         this.data[name] = value;
5921         if(!this.editing && this.store){
5922             this.store.afterEdit(this);
5923         }       
5924     },
5925
5926     /**
5927      * Get the value of the named field.
5928      * @param {String} name The name of the field to get the value of.
5929      * @return {Object} The value of the field.
5930      */
5931     get : function(name){
5932         return this.data[name]; 
5933     },
5934
5935     // private
5936     beginEdit : function(){
5937         this.editing = true;
5938         this.modified = {}; 
5939     },
5940
5941     // private
5942     cancelEdit : function(){
5943         this.editing = false;
5944         delete this.modified;
5945     },
5946
5947     // private
5948     endEdit : function(){
5949         this.editing = false;
5950         if(this.dirty && this.store){
5951             this.store.afterEdit(this);
5952         }
5953     },
5954
5955     /**
5956      * Usually called by the {@link Roo.data.Store} which owns the Record.
5957      * Rejects all changes made to the Record since either creation, or the last commit operation.
5958      * Modified fields are reverted to their original values.
5959      * <p>
5960      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5961      * of reject operations.
5962      */
5963     reject : function(){
5964         var m = this.modified;
5965         for(var n in m){
5966             if(typeof m[n] != "function"){
5967                 this.data[n] = m[n];
5968             }
5969         }
5970         this.dirty = false;
5971         delete this.modified;
5972         this.editing = false;
5973         if(this.store){
5974             this.store.afterReject(this);
5975         }
5976     },
5977
5978     /**
5979      * Usually called by the {@link Roo.data.Store} which owns the Record.
5980      * Commits all changes made to the Record since either creation, or the last commit operation.
5981      * <p>
5982      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5983      * of commit operations.
5984      */
5985     commit : function(){
5986         this.dirty = false;
5987         delete this.modified;
5988         this.editing = false;
5989         if(this.store){
5990             this.store.afterCommit(this);
5991         }
5992     },
5993
5994     // private
5995     hasError : function(){
5996         return this.error != null;
5997     },
5998
5999     // private
6000     clearError : function(){
6001         this.error = null;
6002     },
6003
6004     /**
6005      * Creates a copy of this record.
6006      * @param {String} id (optional) A new record id if you don't want to use this record's id
6007      * @return {Record}
6008      */
6009     copy : function(newId) {
6010         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6011     }
6012 };/*
6013  * Based on:
6014  * Ext JS Library 1.1.1
6015  * Copyright(c) 2006-2007, Ext JS, LLC.
6016  *
6017  * Originally Released Under LGPL - original licence link has changed is not relivant.
6018  *
6019  * Fork - LGPL
6020  * <script type="text/javascript">
6021  */
6022
6023
6024
6025 /**
6026  * @class Roo.data.Store
6027  * @extends Roo.util.Observable
6028  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6029  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6030  * <p>
6031  * 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
6032  * has no knowledge of the format of the data returned by the Proxy.<br>
6033  * <p>
6034  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6035  * instances from the data object. These records are cached and made available through accessor functions.
6036  * @constructor
6037  * Creates a new Store.
6038  * @param {Object} config A config object containing the objects needed for the Store to access data,
6039  * and read the data into Records.
6040  */
6041 Roo.data.Store = function(config){
6042     this.data = new Roo.util.MixedCollection(false);
6043     this.data.getKey = function(o){
6044         return o.id;
6045     };
6046     this.baseParams = {};
6047     // private
6048     this.paramNames = {
6049         "start" : "start",
6050         "limit" : "limit",
6051         "sort" : "sort",
6052         "dir" : "dir",
6053         "multisort" : "_multisort"
6054     };
6055
6056     if(config && config.data){
6057         this.inlineData = config.data;
6058         delete config.data;
6059     }
6060
6061     Roo.apply(this, config);
6062     
6063     if(this.reader){ // reader passed
6064         this.reader = Roo.factory(this.reader, Roo.data);
6065         this.reader.xmodule = this.xmodule || false;
6066         if(!this.recordType){
6067             this.recordType = this.reader.recordType;
6068         }
6069         if(this.reader.onMetaChange){
6070             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6071         }
6072     }
6073
6074     if(this.recordType){
6075         this.fields = this.recordType.prototype.fields;
6076     }
6077     this.modified = [];
6078
6079     this.addEvents({
6080         /**
6081          * @event datachanged
6082          * Fires when the data cache has changed, and a widget which is using this Store
6083          * as a Record cache should refresh its view.
6084          * @param {Store} this
6085          */
6086         datachanged : true,
6087         /**
6088          * @event metachange
6089          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6090          * @param {Store} this
6091          * @param {Object} meta The JSON metadata
6092          */
6093         metachange : true,
6094         /**
6095          * @event add
6096          * Fires when Records have been added to the Store
6097          * @param {Store} this
6098          * @param {Roo.data.Record[]} records The array of Records added
6099          * @param {Number} index The index at which the record(s) were added
6100          */
6101         add : true,
6102         /**
6103          * @event remove
6104          * Fires when a Record has been removed from the Store
6105          * @param {Store} this
6106          * @param {Roo.data.Record} record The Record that was removed
6107          * @param {Number} index The index at which the record was removed
6108          */
6109         remove : true,
6110         /**
6111          * @event update
6112          * Fires when a Record has been updated
6113          * @param {Store} this
6114          * @param {Roo.data.Record} record The Record that was updated
6115          * @param {String} operation The update operation being performed.  Value may be one of:
6116          * <pre><code>
6117  Roo.data.Record.EDIT
6118  Roo.data.Record.REJECT
6119  Roo.data.Record.COMMIT
6120          * </code></pre>
6121          */
6122         update : true,
6123         /**
6124          * @event clear
6125          * Fires when the data cache has been cleared.
6126          * @param {Store} this
6127          */
6128         clear : true,
6129         /**
6130          * @event beforeload
6131          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6132          * the load action will be canceled.
6133          * @param {Store} this
6134          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6135          */
6136         beforeload : true,
6137         /**
6138          * @event beforeloadadd
6139          * Fires after a new set of Records has been loaded.
6140          * @param {Store} this
6141          * @param {Roo.data.Record[]} records The Records that were loaded
6142          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6143          */
6144         beforeloadadd : true,
6145         /**
6146          * @event load
6147          * Fires after a new set of Records has been loaded, before they are added to the store.
6148          * @param {Store} this
6149          * @param {Roo.data.Record[]} records The Records that were loaded
6150          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6151          * @params {Object} return from reader
6152          */
6153         load : true,
6154         /**
6155          * @event loadexception
6156          * Fires if an exception occurs in the Proxy during loading.
6157          * Called with the signature of the Proxy's "loadexception" event.
6158          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6159          * 
6160          * @param {Proxy} 
6161          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6162          * @param {Object} load options 
6163          * @param {Object} jsonData from your request (normally this contains the Exception)
6164          */
6165         loadexception : true
6166     });
6167     
6168     if(this.proxy){
6169         this.proxy = Roo.factory(this.proxy, Roo.data);
6170         this.proxy.xmodule = this.xmodule || false;
6171         this.relayEvents(this.proxy,  ["loadexception"]);
6172     }
6173     this.sortToggle = {};
6174     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6175
6176     Roo.data.Store.superclass.constructor.call(this);
6177
6178     if(this.inlineData){
6179         this.loadData(this.inlineData);
6180         delete this.inlineData;
6181     }
6182 };
6183
6184 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6185      /**
6186     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6187     * without a remote query - used by combo/forms at present.
6188     */
6189     
6190     /**
6191     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6192     */
6193     /**
6194     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6195     */
6196     /**
6197     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6198     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6199     */
6200     /**
6201     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6202     * on any HTTP request
6203     */
6204     /**
6205     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6206     */
6207     /**
6208     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6209     */
6210     multiSort: false,
6211     /**
6212     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6213     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6214     */
6215     remoteSort : false,
6216
6217     /**
6218     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6219      * loaded or when a record is removed. (defaults to false).
6220     */
6221     pruneModifiedRecords : false,
6222
6223     // private
6224     lastOptions : null,
6225
6226     /**
6227      * Add Records to the Store and fires the add event.
6228      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6229      */
6230     add : function(records){
6231         records = [].concat(records);
6232         for(var i = 0, len = records.length; i < len; i++){
6233             records[i].join(this);
6234         }
6235         var index = this.data.length;
6236         this.data.addAll(records);
6237         this.fireEvent("add", this, records, index);
6238     },
6239
6240     /**
6241      * Remove a Record from the Store and fires the remove event.
6242      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6243      */
6244     remove : function(record){
6245         var index = this.data.indexOf(record);
6246         this.data.removeAt(index);
6247         if(this.pruneModifiedRecords){
6248             this.modified.remove(record);
6249         }
6250         this.fireEvent("remove", this, record, index);
6251     },
6252
6253     /**
6254      * Remove all Records from the Store and fires the clear event.
6255      */
6256     removeAll : function(){
6257         this.data.clear();
6258         if(this.pruneModifiedRecords){
6259             this.modified = [];
6260         }
6261         this.fireEvent("clear", this);
6262     },
6263
6264     /**
6265      * Inserts Records to the Store at the given index and fires the add event.
6266      * @param {Number} index The start index at which to insert the passed Records.
6267      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6268      */
6269     insert : function(index, records){
6270         records = [].concat(records);
6271         for(var i = 0, len = records.length; i < len; i++){
6272             this.data.insert(index, records[i]);
6273             records[i].join(this);
6274         }
6275         this.fireEvent("add", this, records, index);
6276     },
6277
6278     /**
6279      * Get the index within the cache of the passed Record.
6280      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6281      * @return {Number} The index of the passed Record. Returns -1 if not found.
6282      */
6283     indexOf : function(record){
6284         return this.data.indexOf(record);
6285     },
6286
6287     /**
6288      * Get the index within the cache of the Record with the passed id.
6289      * @param {String} id The id of the Record to find.
6290      * @return {Number} The index of the Record. Returns -1 if not found.
6291      */
6292     indexOfId : function(id){
6293         return this.data.indexOfKey(id);
6294     },
6295
6296     /**
6297      * Get the Record with the specified id.
6298      * @param {String} id The id of the Record to find.
6299      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6300      */
6301     getById : function(id){
6302         return this.data.key(id);
6303     },
6304
6305     /**
6306      * Get the Record at the specified index.
6307      * @param {Number} index The index of the Record to find.
6308      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6309      */
6310     getAt : function(index){
6311         return this.data.itemAt(index);
6312     },
6313
6314     /**
6315      * Returns a range of Records between specified indices.
6316      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6317      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6318      * @return {Roo.data.Record[]} An array of Records
6319      */
6320     getRange : function(start, end){
6321         return this.data.getRange(start, end);
6322     },
6323
6324     // private
6325     storeOptions : function(o){
6326         o = Roo.apply({}, o);
6327         delete o.callback;
6328         delete o.scope;
6329         this.lastOptions = o;
6330     },
6331
6332     /**
6333      * Loads the Record cache from the configured Proxy using the configured Reader.
6334      * <p>
6335      * If using remote paging, then the first load call must specify the <em>start</em>
6336      * and <em>limit</em> properties in the options.params property to establish the initial
6337      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6338      * <p>
6339      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6340      * and this call will return before the new data has been loaded. Perform any post-processing
6341      * in a callback function, or in a "load" event handler.</strong>
6342      * <p>
6343      * @param {Object} options An object containing properties which control loading options:<ul>
6344      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6345      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6346      * passed the following arguments:<ul>
6347      * <li>r : Roo.data.Record[]</li>
6348      * <li>options: Options object from the load call</li>
6349      * <li>success: Boolean success indicator</li></ul></li>
6350      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6351      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6352      * </ul>
6353      */
6354     load : function(options){
6355         options = options || {};
6356         if(this.fireEvent("beforeload", this, options) !== false){
6357             this.storeOptions(options);
6358             var p = Roo.apply(options.params || {}, this.baseParams);
6359             // if meta was not loaded from remote source.. try requesting it.
6360             if (!this.reader.metaFromRemote) {
6361                 p._requestMeta = 1;
6362             }
6363             if(this.sortInfo && this.remoteSort){
6364                 var pn = this.paramNames;
6365                 p[pn["sort"]] = this.sortInfo.field;
6366                 p[pn["dir"]] = this.sortInfo.direction;
6367             }
6368             if (this.multiSort) {
6369                 var pn = this.paramNames;
6370                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6371             }
6372             
6373             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6374         }
6375     },
6376
6377     /**
6378      * Reloads the Record cache from the configured Proxy using the configured Reader and
6379      * the options from the last load operation performed.
6380      * @param {Object} options (optional) An object containing properties which may override the options
6381      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6382      * the most recently used options are reused).
6383      */
6384     reload : function(options){
6385         this.load(Roo.applyIf(options||{}, this.lastOptions));
6386     },
6387
6388     // private
6389     // Called as a callback by the Reader during a load operation.
6390     loadRecords : function(o, options, success){
6391         if(!o || success === false){
6392             if(success !== false){
6393                 this.fireEvent("load", this, [], options, o);
6394             }
6395             if(options.callback){
6396                 options.callback.call(options.scope || this, [], options, false);
6397             }
6398             return;
6399         }
6400         // if data returned failure - throw an exception.
6401         if (o.success === false) {
6402             // show a message if no listener is registered.
6403             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6404                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6405             }
6406             // loadmask wil be hooked into this..
6407             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6408             return;
6409         }
6410         var r = o.records, t = o.totalRecords || r.length;
6411         
6412         this.fireEvent("beforeloadadd", this, r, options, o);
6413         
6414         if(!options || options.add !== true){
6415             if(this.pruneModifiedRecords){
6416                 this.modified = [];
6417             }
6418             for(var i = 0, len = r.length; i < len; i++){
6419                 r[i].join(this);
6420             }
6421             if(this.snapshot){
6422                 this.data = this.snapshot;
6423                 delete this.snapshot;
6424             }
6425             this.data.clear();
6426             this.data.addAll(r);
6427             this.totalLength = t;
6428             this.applySort();
6429             this.fireEvent("datachanged", this);
6430         }else{
6431             this.totalLength = Math.max(t, this.data.length+r.length);
6432             this.add(r);
6433         }
6434         this.fireEvent("load", this, r, options, o);
6435         if(options.callback){
6436             options.callback.call(options.scope || this, r, options, true);
6437         }
6438     },
6439
6440
6441     /**
6442      * Loads data from a passed data block. A Reader which understands the format of the data
6443      * must have been configured in the constructor.
6444      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6445      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6446      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6447      */
6448     loadData : function(o, append){
6449         var r = this.reader.readRecords(o);
6450         this.loadRecords(r, {add: append}, true);
6451     },
6452
6453     /**
6454      * Gets the number of cached records.
6455      * <p>
6456      * <em>If using paging, this may not be the total size of the dataset. If the data object
6457      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6458      * the data set size</em>
6459      */
6460     getCount : function(){
6461         return this.data.length || 0;
6462     },
6463
6464     /**
6465      * Gets the total number of records in the dataset as returned by the server.
6466      * <p>
6467      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6468      * the dataset size</em>
6469      */
6470     getTotalCount : function(){
6471         return this.totalLength || 0;
6472     },
6473
6474     /**
6475      * Returns the sort state of the Store as an object with two properties:
6476      * <pre><code>
6477  field {String} The name of the field by which the Records are sorted
6478  direction {String} The sort order, "ASC" or "DESC"
6479      * </code></pre>
6480      */
6481     getSortState : function(){
6482         return this.sortInfo;
6483     },
6484
6485     // private
6486     applySort : function(){
6487         if(this.sortInfo && !this.remoteSort){
6488             var s = this.sortInfo, f = s.field;
6489             var st = this.fields.get(f).sortType;
6490             var fn = function(r1, r2){
6491                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6492                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6493             };
6494             this.data.sort(s.direction, fn);
6495             if(this.snapshot && this.snapshot != this.data){
6496                 this.snapshot.sort(s.direction, fn);
6497             }
6498         }
6499     },
6500
6501     /**
6502      * Sets the default sort column and order to be used by the next load operation.
6503      * @param {String} fieldName The name of the field to sort by.
6504      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6505      */
6506     setDefaultSort : function(field, dir){
6507         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6508     },
6509
6510     /**
6511      * Sort the Records.
6512      * If remote sorting is used, the sort is performed on the server, and the cache is
6513      * reloaded. If local sorting is used, the cache is sorted internally.
6514      * @param {String} fieldName The name of the field to sort by.
6515      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6516      */
6517     sort : function(fieldName, dir){
6518         var f = this.fields.get(fieldName);
6519         if(!dir){
6520             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6521             
6522             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6523                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6524             }else{
6525                 dir = f.sortDir;
6526             }
6527         }
6528         this.sortToggle[f.name] = dir;
6529         this.sortInfo = {field: f.name, direction: dir};
6530         if(!this.remoteSort){
6531             this.applySort();
6532             this.fireEvent("datachanged", this);
6533         }else{
6534             this.load(this.lastOptions);
6535         }
6536     },
6537
6538     /**
6539      * Calls the specified function for each of the Records in the cache.
6540      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6541      * Returning <em>false</em> aborts and exits the iteration.
6542      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6543      */
6544     each : function(fn, scope){
6545         this.data.each(fn, scope);
6546     },
6547
6548     /**
6549      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6550      * (e.g., during paging).
6551      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6552      */
6553     getModifiedRecords : function(){
6554         return this.modified;
6555     },
6556
6557     // private
6558     createFilterFn : function(property, value, anyMatch){
6559         if(!value.exec){ // not a regex
6560             value = String(value);
6561             if(value.length == 0){
6562                 return false;
6563             }
6564             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6565         }
6566         return function(r){
6567             return value.test(r.data[property]);
6568         };
6569     },
6570
6571     /**
6572      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6573      * @param {String} property A field on your records
6574      * @param {Number} start The record index to start at (defaults to 0)
6575      * @param {Number} end The last record index to include (defaults to length - 1)
6576      * @return {Number} The sum
6577      */
6578     sum : function(property, start, end){
6579         var rs = this.data.items, v = 0;
6580         start = start || 0;
6581         end = (end || end === 0) ? end : rs.length-1;
6582
6583         for(var i = start; i <= end; i++){
6584             v += (rs[i].data[property] || 0);
6585         }
6586         return v;
6587     },
6588
6589     /**
6590      * Filter the records by a specified property.
6591      * @param {String} field A field on your records
6592      * @param {String/RegExp} value Either a string that the field
6593      * should start with or a RegExp to test against the field
6594      * @param {Boolean} anyMatch True to match any part not just the beginning
6595      */
6596     filter : function(property, value, anyMatch){
6597         var fn = this.createFilterFn(property, value, anyMatch);
6598         return fn ? this.filterBy(fn) : this.clearFilter();
6599     },
6600
6601     /**
6602      * Filter by a function. The specified function will be called with each
6603      * record in this data source. If the function returns true the record is included,
6604      * otherwise it is filtered.
6605      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6606      * @param {Object} scope (optional) The scope of the function (defaults to this)
6607      */
6608     filterBy : function(fn, scope){
6609         this.snapshot = this.snapshot || this.data;
6610         this.data = this.queryBy(fn, scope||this);
6611         this.fireEvent("datachanged", this);
6612     },
6613
6614     /**
6615      * Query the records by a specified property.
6616      * @param {String} field A field on your records
6617      * @param {String/RegExp} value Either a string that the field
6618      * should start with or a RegExp to test against the field
6619      * @param {Boolean} anyMatch True to match any part not just the beginning
6620      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6621      */
6622     query : function(property, value, anyMatch){
6623         var fn = this.createFilterFn(property, value, anyMatch);
6624         return fn ? this.queryBy(fn) : this.data.clone();
6625     },
6626
6627     /**
6628      * Query by a function. The specified function will be called with each
6629      * record in this data source. If the function returns true the record is included
6630      * in the results.
6631      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6632      * @param {Object} scope (optional) The scope of the function (defaults to this)
6633       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6634      **/
6635     queryBy : function(fn, scope){
6636         var data = this.snapshot || this.data;
6637         return data.filterBy(fn, scope||this);
6638     },
6639
6640     /**
6641      * Collects unique values for a particular dataIndex from this store.
6642      * @param {String} dataIndex The property to collect
6643      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6644      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6645      * @return {Array} An array of the unique values
6646      **/
6647     collect : function(dataIndex, allowNull, bypassFilter){
6648         var d = (bypassFilter === true && this.snapshot) ?
6649                 this.snapshot.items : this.data.items;
6650         var v, sv, r = [], l = {};
6651         for(var i = 0, len = d.length; i < len; i++){
6652             v = d[i].data[dataIndex];
6653             sv = String(v);
6654             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6655                 l[sv] = true;
6656                 r[r.length] = v;
6657             }
6658         }
6659         return r;
6660     },
6661
6662     /**
6663      * Revert to a view of the Record cache with no filtering applied.
6664      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6665      */
6666     clearFilter : function(suppressEvent){
6667         if(this.snapshot && this.snapshot != this.data){
6668             this.data = this.snapshot;
6669             delete this.snapshot;
6670             if(suppressEvent !== true){
6671                 this.fireEvent("datachanged", this);
6672             }
6673         }
6674     },
6675
6676     // private
6677     afterEdit : function(record){
6678         if(this.modified.indexOf(record) == -1){
6679             this.modified.push(record);
6680         }
6681         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6682     },
6683     
6684     // private
6685     afterReject : function(record){
6686         this.modified.remove(record);
6687         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6688     },
6689
6690     // private
6691     afterCommit : function(record){
6692         this.modified.remove(record);
6693         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6694     },
6695
6696     /**
6697      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6698      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6699      */
6700     commitChanges : function(){
6701         var m = this.modified.slice(0);
6702         this.modified = [];
6703         for(var i = 0, len = m.length; i < len; i++){
6704             m[i].commit();
6705         }
6706     },
6707
6708     /**
6709      * Cancel outstanding changes on all changed records.
6710      */
6711     rejectChanges : function(){
6712         var m = this.modified.slice(0);
6713         this.modified = [];
6714         for(var i = 0, len = m.length; i < len; i++){
6715             m[i].reject();
6716         }
6717     },
6718
6719     onMetaChange : function(meta, rtype, o){
6720         this.recordType = rtype;
6721         this.fields = rtype.prototype.fields;
6722         delete this.snapshot;
6723         this.sortInfo = meta.sortInfo || this.sortInfo;
6724         this.modified = [];
6725         this.fireEvent('metachange', this, this.reader.meta);
6726     }
6727 });/*
6728  * Based on:
6729  * Ext JS Library 1.1.1
6730  * Copyright(c) 2006-2007, Ext JS, LLC.
6731  *
6732  * Originally Released Under LGPL - original licence link has changed is not relivant.
6733  *
6734  * Fork - LGPL
6735  * <script type="text/javascript">
6736  */
6737
6738 /**
6739  * @class Roo.data.SimpleStore
6740  * @extends Roo.data.Store
6741  * Small helper class to make creating Stores from Array data easier.
6742  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6743  * @cfg {Array} fields An array of field definition objects, or field name strings.
6744  * @cfg {Array} data The multi-dimensional array of data
6745  * @constructor
6746  * @param {Object} config
6747  */
6748 Roo.data.SimpleStore = function(config){
6749     Roo.data.SimpleStore.superclass.constructor.call(this, {
6750         isLocal : true,
6751         reader: new Roo.data.ArrayReader({
6752                 id: config.id
6753             },
6754             Roo.data.Record.create(config.fields)
6755         ),
6756         proxy : new Roo.data.MemoryProxy(config.data)
6757     });
6758     this.load();
6759 };
6760 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6761  * Based on:
6762  * Ext JS Library 1.1.1
6763  * Copyright(c) 2006-2007, Ext JS, LLC.
6764  *
6765  * Originally Released Under LGPL - original licence link has changed is not relivant.
6766  *
6767  * Fork - LGPL
6768  * <script type="text/javascript">
6769  */
6770
6771 /**
6772 /**
6773  * @extends Roo.data.Store
6774  * @class Roo.data.JsonStore
6775  * Small helper class to make creating Stores for JSON data easier. <br/>
6776 <pre><code>
6777 var store = new Roo.data.JsonStore({
6778     url: 'get-images.php',
6779     root: 'images',
6780     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6781 });
6782 </code></pre>
6783  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6784  * JsonReader and HttpProxy (unless inline data is provided).</b>
6785  * @cfg {Array} fields An array of field definition objects, or field name strings.
6786  * @constructor
6787  * @param {Object} config
6788  */
6789 Roo.data.JsonStore = function(c){
6790     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6791         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6792         reader: new Roo.data.JsonReader(c, c.fields)
6793     }));
6794 };
6795 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6796  * Based on:
6797  * Ext JS Library 1.1.1
6798  * Copyright(c) 2006-2007, Ext JS, LLC.
6799  *
6800  * Originally Released Under LGPL - original licence link has changed is not relivant.
6801  *
6802  * Fork - LGPL
6803  * <script type="text/javascript">
6804  */
6805
6806  
6807 Roo.data.Field = function(config){
6808     if(typeof config == "string"){
6809         config = {name: config};
6810     }
6811     Roo.apply(this, config);
6812     
6813     if(!this.type){
6814         this.type = "auto";
6815     }
6816     
6817     var st = Roo.data.SortTypes;
6818     // named sortTypes are supported, here we look them up
6819     if(typeof this.sortType == "string"){
6820         this.sortType = st[this.sortType];
6821     }
6822     
6823     // set default sortType for strings and dates
6824     if(!this.sortType){
6825         switch(this.type){
6826             case "string":
6827                 this.sortType = st.asUCString;
6828                 break;
6829             case "date":
6830                 this.sortType = st.asDate;
6831                 break;
6832             default:
6833                 this.sortType = st.none;
6834         }
6835     }
6836
6837     // define once
6838     var stripRe = /[\$,%]/g;
6839
6840     // prebuilt conversion function for this field, instead of
6841     // switching every time we're reading a value
6842     if(!this.convert){
6843         var cv, dateFormat = this.dateFormat;
6844         switch(this.type){
6845             case "":
6846             case "auto":
6847             case undefined:
6848                 cv = function(v){ return v; };
6849                 break;
6850             case "string":
6851                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6852                 break;
6853             case "int":
6854                 cv = function(v){
6855                     return v !== undefined && v !== null && v !== '' ?
6856                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6857                     };
6858                 break;
6859             case "float":
6860                 cv = function(v){
6861                     return v !== undefined && v !== null && v !== '' ?
6862                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6863                     };
6864                 break;
6865             case "bool":
6866             case "boolean":
6867                 cv = function(v){ return v === true || v === "true" || v == 1; };
6868                 break;
6869             case "date":
6870                 cv = function(v){
6871                     if(!v){
6872                         return '';
6873                     }
6874                     if(v instanceof Date){
6875                         return v;
6876                     }
6877                     if(dateFormat){
6878                         if(dateFormat == "timestamp"){
6879                             return new Date(v*1000);
6880                         }
6881                         return Date.parseDate(v, dateFormat);
6882                     }
6883                     var parsed = Date.parse(v);
6884                     return parsed ? new Date(parsed) : null;
6885                 };
6886              break;
6887             
6888         }
6889         this.convert = cv;
6890     }
6891 };
6892
6893 Roo.data.Field.prototype = {
6894     dateFormat: null,
6895     defaultValue: "",
6896     mapping: null,
6897     sortType : null,
6898     sortDir : "ASC"
6899 };/*
6900  * Based on:
6901  * Ext JS Library 1.1.1
6902  * Copyright(c) 2006-2007, Ext JS, LLC.
6903  *
6904  * Originally Released Under LGPL - original licence link has changed is not relivant.
6905  *
6906  * Fork - LGPL
6907  * <script type="text/javascript">
6908  */
6909  
6910 // Base class for reading structured data from a data source.  This class is intended to be
6911 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6912
6913 /**
6914  * @class Roo.data.DataReader
6915  * Base class for reading structured data from a data source.  This class is intended to be
6916  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6917  */
6918
6919 Roo.data.DataReader = function(meta, recordType){
6920     
6921     this.meta = meta;
6922     
6923     this.recordType = recordType instanceof Array ? 
6924         Roo.data.Record.create(recordType) : recordType;
6925 };
6926
6927 Roo.data.DataReader.prototype = {
6928      /**
6929      * Create an empty record
6930      * @param {Object} data (optional) - overlay some values
6931      * @return {Roo.data.Record} record created.
6932      */
6933     newRow :  function(d) {
6934         var da =  {};
6935         this.recordType.prototype.fields.each(function(c) {
6936             switch( c.type) {
6937                 case 'int' : da[c.name] = 0; break;
6938                 case 'date' : da[c.name] = new Date(); break;
6939                 case 'float' : da[c.name] = 0.0; break;
6940                 case 'boolean' : da[c.name] = false; break;
6941                 default : da[c.name] = ""; break;
6942             }
6943             
6944         });
6945         return new this.recordType(Roo.apply(da, d));
6946     }
6947     
6948 };/*
6949  * Based on:
6950  * Ext JS Library 1.1.1
6951  * Copyright(c) 2006-2007, Ext JS, LLC.
6952  *
6953  * Originally Released Under LGPL - original licence link has changed is not relivant.
6954  *
6955  * Fork - LGPL
6956  * <script type="text/javascript">
6957  */
6958
6959 /**
6960  * @class Roo.data.DataProxy
6961  * @extends Roo.data.Observable
6962  * This class is an abstract base class for implementations which provide retrieval of
6963  * unformatted data objects.<br>
6964  * <p>
6965  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6966  * (of the appropriate type which knows how to parse the data object) to provide a block of
6967  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6968  * <p>
6969  * Custom implementations must implement the load method as described in
6970  * {@link Roo.data.HttpProxy#load}.
6971  */
6972 Roo.data.DataProxy = function(){
6973     this.addEvents({
6974         /**
6975          * @event beforeload
6976          * Fires before a network request is made to retrieve a data object.
6977          * @param {Object} This DataProxy object.
6978          * @param {Object} params The params parameter to the load function.
6979          */
6980         beforeload : true,
6981         /**
6982          * @event load
6983          * Fires before the load method's callback is called.
6984          * @param {Object} This DataProxy object.
6985          * @param {Object} o The data object.
6986          * @param {Object} arg The callback argument object passed to the load function.
6987          */
6988         load : true,
6989         /**
6990          * @event loadexception
6991          * Fires if an Exception occurs during data retrieval.
6992          * @param {Object} This DataProxy object.
6993          * @param {Object} o The data object.
6994          * @param {Object} arg The callback argument object passed to the load function.
6995          * @param {Object} e The Exception.
6996          */
6997         loadexception : true
6998     });
6999     Roo.data.DataProxy.superclass.constructor.call(this);
7000 };
7001
7002 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7003
7004     /**
7005      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7006      */
7007 /*
7008  * Based on:
7009  * Ext JS Library 1.1.1
7010  * Copyright(c) 2006-2007, Ext JS, LLC.
7011  *
7012  * Originally Released Under LGPL - original licence link has changed is not relivant.
7013  *
7014  * Fork - LGPL
7015  * <script type="text/javascript">
7016  */
7017 /**
7018  * @class Roo.data.MemoryProxy
7019  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7020  * to the Reader when its load method is called.
7021  * @constructor
7022  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7023  */
7024 Roo.data.MemoryProxy = function(data){
7025     if (data.data) {
7026         data = data.data;
7027     }
7028     Roo.data.MemoryProxy.superclass.constructor.call(this);
7029     this.data = data;
7030 };
7031
7032 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7033     /**
7034      * Load data from the requested source (in this case an in-memory
7035      * data object passed to the constructor), read the data object into
7036      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7037      * process that block using the passed callback.
7038      * @param {Object} params This parameter is not used by the MemoryProxy class.
7039      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7040      * object into a block of Roo.data.Records.
7041      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7042      * The function must be passed <ul>
7043      * <li>The Record block object</li>
7044      * <li>The "arg" argument from the load function</li>
7045      * <li>A boolean success indicator</li>
7046      * </ul>
7047      * @param {Object} scope The scope in which to call the callback
7048      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7049      */
7050     load : function(params, reader, callback, scope, arg){
7051         params = params || {};
7052         var result;
7053         try {
7054             result = reader.readRecords(this.data);
7055         }catch(e){
7056             this.fireEvent("loadexception", this, arg, null, e);
7057             callback.call(scope, null, arg, false);
7058             return;
7059         }
7060         callback.call(scope, result, arg, true);
7061     },
7062     
7063     // private
7064     update : function(params, records){
7065         
7066     }
7067 });/*
7068  * Based on:
7069  * Ext JS Library 1.1.1
7070  * Copyright(c) 2006-2007, Ext JS, LLC.
7071  *
7072  * Originally Released Under LGPL - original licence link has changed is not relivant.
7073  *
7074  * Fork - LGPL
7075  * <script type="text/javascript">
7076  */
7077 /**
7078  * @class Roo.data.HttpProxy
7079  * @extends Roo.data.DataProxy
7080  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7081  * configured to reference a certain URL.<br><br>
7082  * <p>
7083  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7084  * from which the running page was served.<br><br>
7085  * <p>
7086  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7087  * <p>
7088  * Be aware that to enable the browser to parse an XML document, the server must set
7089  * the Content-Type header in the HTTP response to "text/xml".
7090  * @constructor
7091  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7092  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7093  * will be used to make the request.
7094  */
7095 Roo.data.HttpProxy = function(conn){
7096     Roo.data.HttpProxy.superclass.constructor.call(this);
7097     // is conn a conn config or a real conn?
7098     this.conn = conn;
7099     this.useAjax = !conn || !conn.events;
7100   
7101 };
7102
7103 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7104     // thse are take from connection...
7105     
7106     /**
7107      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7108      */
7109     /**
7110      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7111      * extra parameters to each request made by this object. (defaults to undefined)
7112      */
7113     /**
7114      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7115      *  to each request made by this object. (defaults to undefined)
7116      */
7117     /**
7118      * @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)
7119      */
7120     /**
7121      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7122      */
7123      /**
7124      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7125      * @type Boolean
7126      */
7127   
7128
7129     /**
7130      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7131      * @type Boolean
7132      */
7133     /**
7134      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7135      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7136      * a finer-grained basis than the DataProxy events.
7137      */
7138     getConnection : function(){
7139         return this.useAjax ? Roo.Ajax : this.conn;
7140     },
7141
7142     /**
7143      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7144      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7145      * process that block using the passed callback.
7146      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7147      * for the request to the remote server.
7148      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7149      * object into a block of Roo.data.Records.
7150      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7151      * The function must be passed <ul>
7152      * <li>The Record block object</li>
7153      * <li>The "arg" argument from the load function</li>
7154      * <li>A boolean success indicator</li>
7155      * </ul>
7156      * @param {Object} scope The scope in which to call the callback
7157      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7158      */
7159     load : function(params, reader, callback, scope, arg){
7160         if(this.fireEvent("beforeload", this, params) !== false){
7161             var  o = {
7162                 params : params || {},
7163                 request: {
7164                     callback : callback,
7165                     scope : scope,
7166                     arg : arg
7167                 },
7168                 reader: reader,
7169                 callback : this.loadResponse,
7170                 scope: this
7171             };
7172             if(this.useAjax){
7173                 Roo.applyIf(o, this.conn);
7174                 if(this.activeRequest){
7175                     Roo.Ajax.abort(this.activeRequest);
7176                 }
7177                 this.activeRequest = Roo.Ajax.request(o);
7178             }else{
7179                 this.conn.request(o);
7180             }
7181         }else{
7182             callback.call(scope||this, null, arg, false);
7183         }
7184     },
7185
7186     // private
7187     loadResponse : function(o, success, response){
7188         delete this.activeRequest;
7189         if(!success){
7190             this.fireEvent("loadexception", this, o, response);
7191             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7192             return;
7193         }
7194         var result;
7195         try {
7196             result = o.reader.read(response);
7197         }catch(e){
7198             this.fireEvent("loadexception", this, o, response, e);
7199             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7200             return;
7201         }
7202         
7203         this.fireEvent("load", this, o, o.request.arg);
7204         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7205     },
7206
7207     // private
7208     update : function(dataSet){
7209
7210     },
7211
7212     // private
7213     updateResponse : function(dataSet){
7214
7215     }
7216 });/*
7217  * Based on:
7218  * Ext JS Library 1.1.1
7219  * Copyright(c) 2006-2007, Ext JS, LLC.
7220  *
7221  * Originally Released Under LGPL - original licence link has changed is not relivant.
7222  *
7223  * Fork - LGPL
7224  * <script type="text/javascript">
7225  */
7226
7227 /**
7228  * @class Roo.data.ScriptTagProxy
7229  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7230  * other than the originating domain of the running page.<br><br>
7231  * <p>
7232  * <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
7233  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7234  * <p>
7235  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7236  * source code that is used as the source inside a &lt;script> tag.<br><br>
7237  * <p>
7238  * In order for the browser to process the returned data, the server must wrap the data object
7239  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7240  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7241  * depending on whether the callback name was passed:
7242  * <p>
7243  * <pre><code>
7244 boolean scriptTag = false;
7245 String cb = request.getParameter("callback");
7246 if (cb != null) {
7247     scriptTag = true;
7248     response.setContentType("text/javascript");
7249 } else {
7250     response.setContentType("application/x-json");
7251 }
7252 Writer out = response.getWriter();
7253 if (scriptTag) {
7254     out.write(cb + "(");
7255 }
7256 out.print(dataBlock.toJsonString());
7257 if (scriptTag) {
7258     out.write(");");
7259 }
7260 </pre></code>
7261  *
7262  * @constructor
7263  * @param {Object} config A configuration object.
7264  */
7265 Roo.data.ScriptTagProxy = function(config){
7266     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7267     Roo.apply(this, config);
7268     this.head = document.getElementsByTagName("head")[0];
7269 };
7270
7271 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7272
7273 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7274     /**
7275      * @cfg {String} url The URL from which to request the data object.
7276      */
7277     /**
7278      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7279      */
7280     timeout : 30000,
7281     /**
7282      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7283      * the server the name of the callback function set up by the load call to process the returned data object.
7284      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7285      * javascript output which calls this named function passing the data object as its only parameter.
7286      */
7287     callbackParam : "callback",
7288     /**
7289      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7290      * name to the request.
7291      */
7292     nocache : true,
7293
7294     /**
7295      * Load data from the configured URL, read the data object into
7296      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7297      * process that block using the passed callback.
7298      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7299      * for the request to the remote server.
7300      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7301      * object into a block of Roo.data.Records.
7302      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7303      * The function must be passed <ul>
7304      * <li>The Record block object</li>
7305      * <li>The "arg" argument from the load function</li>
7306      * <li>A boolean success indicator</li>
7307      * </ul>
7308      * @param {Object} scope The scope in which to call the callback
7309      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7310      */
7311     load : function(params, reader, callback, scope, arg){
7312         if(this.fireEvent("beforeload", this, params) !== false){
7313
7314             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7315
7316             var url = this.url;
7317             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7318             if(this.nocache){
7319                 url += "&_dc=" + (new Date().getTime());
7320             }
7321             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7322             var trans = {
7323                 id : transId,
7324                 cb : "stcCallback"+transId,
7325                 scriptId : "stcScript"+transId,
7326                 params : params,
7327                 arg : arg,
7328                 url : url,
7329                 callback : callback,
7330                 scope : scope,
7331                 reader : reader
7332             };
7333             var conn = this;
7334
7335             window[trans.cb] = function(o){
7336                 conn.handleResponse(o, trans);
7337             };
7338
7339             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7340
7341             if(this.autoAbort !== false){
7342                 this.abort();
7343             }
7344
7345             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7346
7347             var script = document.createElement("script");
7348             script.setAttribute("src", url);
7349             script.setAttribute("type", "text/javascript");
7350             script.setAttribute("id", trans.scriptId);
7351             this.head.appendChild(script);
7352
7353             this.trans = trans;
7354         }else{
7355             callback.call(scope||this, null, arg, false);
7356         }
7357     },
7358
7359     // private
7360     isLoading : function(){
7361         return this.trans ? true : false;
7362     },
7363
7364     /**
7365      * Abort the current server request.
7366      */
7367     abort : function(){
7368         if(this.isLoading()){
7369             this.destroyTrans(this.trans);
7370         }
7371     },
7372
7373     // private
7374     destroyTrans : function(trans, isLoaded){
7375         this.head.removeChild(document.getElementById(trans.scriptId));
7376         clearTimeout(trans.timeoutId);
7377         if(isLoaded){
7378             window[trans.cb] = undefined;
7379             try{
7380                 delete window[trans.cb];
7381             }catch(e){}
7382         }else{
7383             // if hasn't been loaded, wait for load to remove it to prevent script error
7384             window[trans.cb] = function(){
7385                 window[trans.cb] = undefined;
7386                 try{
7387                     delete window[trans.cb];
7388                 }catch(e){}
7389             };
7390         }
7391     },
7392
7393     // private
7394     handleResponse : function(o, trans){
7395         this.trans = false;
7396         this.destroyTrans(trans, true);
7397         var result;
7398         try {
7399             result = trans.reader.readRecords(o);
7400         }catch(e){
7401             this.fireEvent("loadexception", this, o, trans.arg, e);
7402             trans.callback.call(trans.scope||window, null, trans.arg, false);
7403             return;
7404         }
7405         this.fireEvent("load", this, o, trans.arg);
7406         trans.callback.call(trans.scope||window, result, trans.arg, true);
7407     },
7408
7409     // private
7410     handleFailure : function(trans){
7411         this.trans = false;
7412         this.destroyTrans(trans, false);
7413         this.fireEvent("loadexception", this, null, trans.arg);
7414         trans.callback.call(trans.scope||window, null, trans.arg, false);
7415     }
7416 });/*
7417  * Based on:
7418  * Ext JS Library 1.1.1
7419  * Copyright(c) 2006-2007, Ext JS, LLC.
7420  *
7421  * Originally Released Under LGPL - original licence link has changed is not relivant.
7422  *
7423  * Fork - LGPL
7424  * <script type="text/javascript">
7425  */
7426
7427 /**
7428  * @class Roo.data.JsonReader
7429  * @extends Roo.data.DataReader
7430  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7431  * based on mappings in a provided Roo.data.Record constructor.
7432  * 
7433  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7434  * in the reply previously. 
7435  * 
7436  * <p>
7437  * Example code:
7438  * <pre><code>
7439 var RecordDef = Roo.data.Record.create([
7440     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7441     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7442 ]);
7443 var myReader = new Roo.data.JsonReader({
7444     totalProperty: "results",    // The property which contains the total dataset size (optional)
7445     root: "rows",                // The property which contains an Array of row objects
7446     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7447 }, RecordDef);
7448 </code></pre>
7449  * <p>
7450  * This would consume a JSON file like this:
7451  * <pre><code>
7452 { 'results': 2, 'rows': [
7453     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7454     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7455 }
7456 </code></pre>
7457  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7458  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7459  * paged from the remote server.
7460  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7461  * @cfg {String} root name of the property which contains the Array of row objects.
7462  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7463  * @constructor
7464  * Create a new JsonReader
7465  * @param {Object} meta Metadata configuration options
7466  * @param {Object} recordType Either an Array of field definition objects,
7467  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7468  */
7469 Roo.data.JsonReader = function(meta, recordType){
7470     
7471     meta = meta || {};
7472     // set some defaults:
7473     Roo.applyIf(meta, {
7474         totalProperty: 'total',
7475         successProperty : 'success',
7476         root : 'data',
7477         id : 'id'
7478     });
7479     
7480     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7481 };
7482 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7483     
7484     /**
7485      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7486      * Used by Store query builder to append _requestMeta to params.
7487      * 
7488      */
7489     metaFromRemote : false,
7490     /**
7491      * This method is only used by a DataProxy which has retrieved data from a remote server.
7492      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7493      * @return {Object} data A data block which is used by an Roo.data.Store object as
7494      * a cache of Roo.data.Records.
7495      */
7496     read : function(response){
7497         var json = response.responseText;
7498        
7499         var o = /* eval:var:o */ eval("("+json+")");
7500         if(!o) {
7501             throw {message: "JsonReader.read: Json object not found"};
7502         }
7503         
7504         if(o.metaData){
7505             
7506             delete this.ef;
7507             this.metaFromRemote = true;
7508             this.meta = o.metaData;
7509             this.recordType = Roo.data.Record.create(o.metaData.fields);
7510             this.onMetaChange(this.meta, this.recordType, o);
7511         }
7512         return this.readRecords(o);
7513     },
7514
7515     // private function a store will implement
7516     onMetaChange : function(meta, recordType, o){
7517
7518     },
7519
7520     /**
7521          * @ignore
7522          */
7523     simpleAccess: function(obj, subsc) {
7524         return obj[subsc];
7525     },
7526
7527         /**
7528          * @ignore
7529          */
7530     getJsonAccessor: function(){
7531         var re = /[\[\.]/;
7532         return function(expr) {
7533             try {
7534                 return(re.test(expr))
7535                     ? new Function("obj", "return obj." + expr)
7536                     : function(obj){
7537                         return obj[expr];
7538                     };
7539             } catch(e){}
7540             return Roo.emptyFn;
7541         };
7542     }(),
7543
7544     /**
7545      * Create a data block containing Roo.data.Records from an XML document.
7546      * @param {Object} o An object which contains an Array of row objects in the property specified
7547      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7548      * which contains the total size of the dataset.
7549      * @return {Object} data A data block which is used by an Roo.data.Store object as
7550      * a cache of Roo.data.Records.
7551      */
7552     readRecords : function(o){
7553         /**
7554          * After any data loads, the raw JSON data is available for further custom processing.
7555          * @type Object
7556          */
7557         this.o = o;
7558         var s = this.meta, Record = this.recordType,
7559             f = Record.prototype.fields, fi = f.items, fl = f.length;
7560
7561 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7562         if (!this.ef) {
7563             if(s.totalProperty) {
7564                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7565                 }
7566                 if(s.successProperty) {
7567                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7568                 }
7569                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7570                 if (s.id) {
7571                         var g = this.getJsonAccessor(s.id);
7572                         this.getId = function(rec) {
7573                                 var r = g(rec);
7574                                 return (r === undefined || r === "") ? null : r;
7575                         };
7576                 } else {
7577                         this.getId = function(){return null;};
7578                 }
7579             this.ef = [];
7580             for(var jj = 0; jj < fl; jj++){
7581                 f = fi[jj];
7582                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7583                 this.ef[jj] = this.getJsonAccessor(map);
7584             }
7585         }
7586
7587         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7588         if(s.totalProperty){
7589             var vt = parseInt(this.getTotal(o), 10);
7590             if(!isNaN(vt)){
7591                 totalRecords = vt;
7592             }
7593         }
7594         if(s.successProperty){
7595             var vs = this.getSuccess(o);
7596             if(vs === false || vs === 'false'){
7597                 success = false;
7598             }
7599         }
7600         var records = [];
7601             for(var i = 0; i < c; i++){
7602                     var n = root[i];
7603                 var values = {};
7604                 var id = this.getId(n);
7605                 for(var j = 0; j < fl; j++){
7606                     f = fi[j];
7607                 var v = this.ef[j](n);
7608                 if (!f.convert) {
7609                     Roo.log('missing convert for ' + f.name);
7610                     Roo.log(f);
7611                     continue;
7612                 }
7613                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7614                 }
7615                 var record = new Record(values, id);
7616                 record.json = n;
7617                 records[i] = record;
7618             }
7619             return {
7620             raw : o,
7621                 success : success,
7622                 records : records,
7623                 totalRecords : totalRecords
7624             };
7625     }
7626 });/*
7627  * Based on:
7628  * Ext JS Library 1.1.1
7629  * Copyright(c) 2006-2007, Ext JS, LLC.
7630  *
7631  * Originally Released Under LGPL - original licence link has changed is not relivant.
7632  *
7633  * Fork - LGPL
7634  * <script type="text/javascript">
7635  */
7636
7637 /**
7638  * @class Roo.data.ArrayReader
7639  * @extends Roo.data.DataReader
7640  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7641  * Each element of that Array represents a row of data fields. The
7642  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7643  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7644  * <p>
7645  * Example code:.
7646  * <pre><code>
7647 var RecordDef = Roo.data.Record.create([
7648     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7649     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7650 ]);
7651 var myReader = new Roo.data.ArrayReader({
7652     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7653 }, RecordDef);
7654 </code></pre>
7655  * <p>
7656  * This would consume an Array like this:
7657  * <pre><code>
7658 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7659   </code></pre>
7660  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7661  * @constructor
7662  * Create a new JsonReader
7663  * @param {Object} meta Metadata configuration options.
7664  * @param {Object} recordType Either an Array of field definition objects
7665  * as specified to {@link Roo.data.Record#create},
7666  * or an {@link Roo.data.Record} object
7667  * created using {@link Roo.data.Record#create}.
7668  */
7669 Roo.data.ArrayReader = function(meta, recordType){
7670     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7671 };
7672
7673 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7674     /**
7675      * Create a data block containing Roo.data.Records from an XML document.
7676      * @param {Object} o An Array of row objects which represents the dataset.
7677      * @return {Object} data A data block which is used by an Roo.data.Store object as
7678      * a cache of Roo.data.Records.
7679      */
7680     readRecords : function(o){
7681         var sid = this.meta ? this.meta.id : null;
7682         var recordType = this.recordType, fields = recordType.prototype.fields;
7683         var records = [];
7684         var root = o;
7685             for(var i = 0; i < root.length; i++){
7686                     var n = root[i];
7687                 var values = {};
7688                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7689                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7690                 var f = fields.items[j];
7691                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7692                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7693                 v = f.convert(v);
7694                 values[f.name] = v;
7695             }
7696                 var record = new recordType(values, id);
7697                 record.json = n;
7698                 records[records.length] = record;
7699             }
7700             return {
7701                 records : records,
7702                 totalRecords : records.length
7703             };
7704     }
7705 });/*
7706  * - LGPL
7707  * * 
7708  */
7709
7710 /**
7711  * @class Roo.bootstrap.ComboBox
7712  * @extends Roo.bootstrap.TriggerField
7713  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7714  * @cfg {Boolean} append (true|false) default false
7715  * @constructor
7716  * Create a new ComboBox.
7717  * @param {Object} config Configuration options
7718  */
7719 Roo.bootstrap.ComboBox = function(config){
7720     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7721     this.addEvents({
7722         /**
7723          * @event expand
7724          * Fires when the dropdown list is expanded
7725              * @param {Roo.bootstrap.ComboBox} combo This combo box
7726              */
7727         'expand' : true,
7728         /**
7729          * @event collapse
7730          * Fires when the dropdown list is collapsed
7731              * @param {Roo.bootstrap.ComboBox} combo This combo box
7732              */
7733         'collapse' : true,
7734         /**
7735          * @event beforeselect
7736          * Fires before a list item is selected. Return false to cancel the selection.
7737              * @param {Roo.bootstrap.ComboBox} combo This combo box
7738              * @param {Roo.data.Record} record The data record returned from the underlying store
7739              * @param {Number} index The index of the selected item in the dropdown list
7740              */
7741         'beforeselect' : true,
7742         /**
7743          * @event select
7744          * Fires when a list item is selected
7745              * @param {Roo.bootstrap.ComboBox} combo This combo box
7746              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7747              * @param {Number} index The index of the selected item in the dropdown list
7748              */
7749         'select' : true,
7750         /**
7751          * @event beforequery
7752          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7753          * The event object passed has these properties:
7754              * @param {Roo.bootstrap.ComboBox} combo This combo box
7755              * @param {String} query The query
7756              * @param {Boolean} forceAll true to force "all" query
7757              * @param {Boolean} cancel true to cancel the query
7758              * @param {Object} e The query event object
7759              */
7760         'beforequery': true,
7761          /**
7762          * @event add
7763          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7764              * @param {Roo.bootstrap.ComboBox} combo This combo box
7765              */
7766         'add' : true,
7767         /**
7768          * @event edit
7769          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7770              * @param {Roo.bootstrap.ComboBox} combo This combo box
7771              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7772              */
7773         'edit' : true,
7774         /**
7775          * @event remove
7776          * Fires when the remove value from the combobox array
7777              * @param {Roo.bootstrap.ComboBox} combo This combo box
7778              */
7779         'remove' : true
7780         
7781     });
7782     
7783     
7784     this.selectedIndex = -1;
7785     if(this.mode == 'local'){
7786         if(config.queryDelay === undefined){
7787             this.queryDelay = 10;
7788         }
7789         if(config.minChars === undefined){
7790             this.minChars = 0;
7791         }
7792     }
7793 };
7794
7795 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7796      
7797     /**
7798      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7799      * rendering into an Roo.Editor, defaults to false)
7800      */
7801     /**
7802      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7803      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7804      */
7805     /**
7806      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7807      */
7808     /**
7809      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7810      * the dropdown list (defaults to undefined, with no header element)
7811      */
7812
7813      /**
7814      * @cfg {String/Roo.Template} tpl The template to use to render the output
7815      */
7816      
7817      /**
7818      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7819      */
7820     listWidth: undefined,
7821     /**
7822      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7823      * mode = 'remote' or 'text' if mode = 'local')
7824      */
7825     displayField: undefined,
7826     /**
7827      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7828      * mode = 'remote' or 'value' if mode = 'local'). 
7829      * Note: use of a valueField requires the user make a selection
7830      * in order for a value to be mapped.
7831      */
7832     valueField: undefined,
7833     
7834     
7835     /**
7836      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7837      * field's data value (defaults to the underlying DOM element's name)
7838      */
7839     hiddenName: undefined,
7840     /**
7841      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7842      */
7843     listClass: '',
7844     /**
7845      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7846      */
7847     selectedClass: 'active',
7848     
7849     /**
7850      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7851      */
7852     shadow:'sides',
7853     /**
7854      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7855      * anchor positions (defaults to 'tl-bl')
7856      */
7857     listAlign: 'tl-bl?',
7858     /**
7859      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7860      */
7861     maxHeight: 300,
7862     /**
7863      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7864      * query specified by the allQuery config option (defaults to 'query')
7865      */
7866     triggerAction: 'query',
7867     /**
7868      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7869      * (defaults to 4, does not apply if editable = false)
7870      */
7871     minChars : 4,
7872     /**
7873      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7874      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7875      */
7876     typeAhead: false,
7877     /**
7878      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7879      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7880      */
7881     queryDelay: 500,
7882     /**
7883      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7884      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7885      */
7886     pageSize: 0,
7887     /**
7888      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7889      * when editable = true (defaults to false)
7890      */
7891     selectOnFocus:false,
7892     /**
7893      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7894      */
7895     queryParam: 'query',
7896     /**
7897      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7898      * when mode = 'remote' (defaults to 'Loading...')
7899      */
7900     loadingText: 'Loading...',
7901     /**
7902      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7903      */
7904     resizable: false,
7905     /**
7906      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7907      */
7908     handleHeight : 8,
7909     /**
7910      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7911      * traditional select (defaults to true)
7912      */
7913     editable: true,
7914     /**
7915      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7916      */
7917     allQuery: '',
7918     /**
7919      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7920      */
7921     mode: 'remote',
7922     /**
7923      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7924      * listWidth has a higher value)
7925      */
7926     minListWidth : 70,
7927     /**
7928      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7929      * allow the user to set arbitrary text into the field (defaults to false)
7930      */
7931     forceSelection:false,
7932     /**
7933      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7934      * if typeAhead = true (defaults to 250)
7935      */
7936     typeAheadDelay : 250,
7937     /**
7938      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7939      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7940      */
7941     valueNotFoundText : undefined,
7942     /**
7943      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7944      */
7945     blockFocus : false,
7946     
7947     /**
7948      * @cfg {Boolean} disableClear Disable showing of clear button.
7949      */
7950     disableClear : false,
7951     /**
7952      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7953      */
7954     alwaysQuery : false,
7955     
7956     /**
7957      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7958      */
7959     multiple : false,
7960     
7961     //private
7962     addicon : false,
7963     editicon: false,
7964     
7965     page: 0,
7966     hasQuery: false,
7967     append: false,
7968     loadNext: false,
7969     item: [],
7970     
7971     // element that contains real text value.. (when hidden is used..)
7972      
7973     // private
7974     initEvents: function(){
7975         
7976         if (!this.store) {
7977             throw "can not find store for combo";
7978         }
7979         this.store = Roo.factory(this.store, Roo.data);
7980         
7981         
7982         
7983         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
7984         
7985         
7986         if(this.hiddenName){
7987             
7988             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
7989             
7990             this.hiddenField.dom.value =
7991                 this.hiddenValue !== undefined ? this.hiddenValue :
7992                 this.value !== undefined ? this.value : '';
7993
7994             // prevent input submission
7995             this.el.dom.removeAttribute('name');
7996             this.hiddenField.dom.setAttribute('name', this.hiddenName);
7997              
7998              
7999         }
8000         //if(Roo.isGecko){
8001         //    this.el.dom.setAttribute('autocomplete', 'off');
8002         //}
8003
8004         var cls = 'x-combo-list';
8005         this.list = this.el.select('ul.dropdown-menu',true).first();
8006
8007         //this.list = new Roo.Layer({
8008         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8009         //});
8010         
8011         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8012         this.list.setWidth(lw);
8013         
8014         this.list.on('mouseover', this.onViewOver, this);
8015         this.list.on('mousemove', this.onViewMove, this);
8016         
8017         this.list.on('scroll', this.onViewScroll, this);
8018         
8019         /*
8020         this.list.swallowEvent('mousewheel');
8021         this.assetHeight = 0;
8022
8023         if(this.title){
8024             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8025             this.assetHeight += this.header.getHeight();
8026         }
8027
8028         this.innerList = this.list.createChild({cls:cls+'-inner'});
8029         this.innerList.on('mouseover', this.onViewOver, this);
8030         this.innerList.on('mousemove', this.onViewMove, this);
8031         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8032         
8033         if(this.allowBlank && !this.pageSize && !this.disableClear){
8034             this.footer = this.list.createChild({cls:cls+'-ft'});
8035             this.pageTb = new Roo.Toolbar(this.footer);
8036            
8037         }
8038         if(this.pageSize){
8039             this.footer = this.list.createChild({cls:cls+'-ft'});
8040             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8041                     {pageSize: this.pageSize});
8042             
8043         }
8044         
8045         if (this.pageTb && this.allowBlank && !this.disableClear) {
8046             var _this = this;
8047             this.pageTb.add(new Roo.Toolbar.Fill(), {
8048                 cls: 'x-btn-icon x-btn-clear',
8049                 text: '&#160;',
8050                 handler: function()
8051                 {
8052                     _this.collapse();
8053                     _this.clearValue();
8054                     _this.onSelect(false, -1);
8055                 }
8056             });
8057         }
8058         if (this.footer) {
8059             this.assetHeight += this.footer.getHeight();
8060         }
8061         */
8062             
8063         if(!this.tpl){
8064             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8065         }
8066
8067         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8068             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8069         });
8070         //this.view.wrapEl.setDisplayed(false);
8071         this.view.on('click', this.onViewClick, this);
8072         
8073         
8074         
8075         this.store.on('beforeload', this.onBeforeLoad, this);
8076         this.store.on('load', this.onLoad, this);
8077         this.store.on('loadexception', this.onLoadException, this);
8078         /*
8079         if(this.resizable){
8080             this.resizer = new Roo.Resizable(this.list,  {
8081                pinned:true, handles:'se'
8082             });
8083             this.resizer.on('resize', function(r, w, h){
8084                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8085                 this.listWidth = w;
8086                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8087                 this.restrictHeight();
8088             }, this);
8089             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8090         }
8091         */
8092         if(!this.editable){
8093             this.editable = true;
8094             this.setEditable(false);
8095         }
8096         
8097         /*
8098         
8099         if (typeof(this.events.add.listeners) != 'undefined') {
8100             
8101             this.addicon = this.wrap.createChild(
8102                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8103        
8104             this.addicon.on('click', function(e) {
8105                 this.fireEvent('add', this);
8106             }, this);
8107         }
8108         if (typeof(this.events.edit.listeners) != 'undefined') {
8109             
8110             this.editicon = this.wrap.createChild(
8111                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8112             if (this.addicon) {
8113                 this.editicon.setStyle('margin-left', '40px');
8114             }
8115             this.editicon.on('click', function(e) {
8116                 
8117                 // we fire even  if inothing is selected..
8118                 this.fireEvent('edit', this, this.lastData );
8119                 
8120             }, this);
8121         }
8122         */
8123         
8124         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8125             "up" : function(e){
8126                 this.inKeyMode = true;
8127                 this.selectPrev();
8128             },
8129
8130             "down" : function(e){
8131                 if(!this.isExpanded()){
8132                     this.onTriggerClick();
8133                 }else{
8134                     this.inKeyMode = true;
8135                     this.selectNext();
8136                 }
8137             },
8138
8139             "enter" : function(e){
8140                 this.onViewClick();
8141                 //return true;
8142             },
8143
8144             "esc" : function(e){
8145                 this.collapse();
8146             },
8147
8148             "tab" : function(e){
8149                 this.collapse();
8150                 
8151                 if(this.fireEvent("specialkey", this, e)){
8152                     this.onViewClick(false);
8153                 }
8154                 
8155                 return true;
8156             },
8157
8158             scope : this,
8159
8160             doRelay : function(foo, bar, hname){
8161                 if(hname == 'down' || this.scope.isExpanded()){
8162                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8163                 }
8164                 return true;
8165             },
8166
8167             forceKeyDown: true
8168         });
8169         
8170         
8171         this.queryDelay = Math.max(this.queryDelay || 10,
8172                 this.mode == 'local' ? 10 : 250);
8173         
8174         
8175         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8176         
8177         if(this.typeAhead){
8178             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8179         }
8180         if(this.editable !== false){
8181             this.inputEl().on("keyup", this.onKeyUp, this);
8182         }
8183         if(this.forceSelection){
8184             this.on('blur', this.doForce, this);
8185         }
8186         
8187         if(this.multiple){
8188             this.choices = this.el.select('ul.select2-choices', true).first();
8189             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8190         }
8191     },
8192
8193     onDestroy : function(){
8194         if(this.view){
8195             this.view.setStore(null);
8196             this.view.el.removeAllListeners();
8197             this.view.el.remove();
8198             this.view.purgeListeners();
8199         }
8200         if(this.list){
8201             this.list.dom.innerHTML  = '';
8202         }
8203         if(this.store){
8204             this.store.un('beforeload', this.onBeforeLoad, this);
8205             this.store.un('load', this.onLoad, this);
8206             this.store.un('loadexception', this.onLoadException, this);
8207         }
8208         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8209     },
8210
8211     // private
8212     fireKey : function(e){
8213         if(e.isNavKeyPress() && !this.list.isVisible()){
8214             this.fireEvent("specialkey", this, e);
8215         }
8216     },
8217
8218     // private
8219     onResize: function(w, h){
8220 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8221 //        
8222 //        if(typeof w != 'number'){
8223 //            // we do not handle it!?!?
8224 //            return;
8225 //        }
8226 //        var tw = this.trigger.getWidth();
8227 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8228 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8229 //        var x = w - tw;
8230 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8231 //            
8232 //        //this.trigger.setStyle('left', x+'px');
8233 //        
8234 //        if(this.list && this.listWidth === undefined){
8235 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8236 //            this.list.setWidth(lw);
8237 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8238 //        }
8239         
8240     
8241         
8242     },
8243
8244     /**
8245      * Allow or prevent the user from directly editing the field text.  If false is passed,
8246      * the user will only be able to select from the items defined in the dropdown list.  This method
8247      * is the runtime equivalent of setting the 'editable' config option at config time.
8248      * @param {Boolean} value True to allow the user to directly edit the field text
8249      */
8250     setEditable : function(value){
8251         if(value == this.editable){
8252             return;
8253         }
8254         this.editable = value;
8255         if(!value){
8256             this.inputEl().dom.setAttribute('readOnly', true);
8257             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8258             this.inputEl().addClass('x-combo-noedit');
8259         }else{
8260             this.inputEl().dom.setAttribute('readOnly', false);
8261             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8262             this.inputEl().removeClass('x-combo-noedit');
8263         }
8264     },
8265
8266     // private
8267     
8268     onBeforeLoad : function(combo,opts){
8269         if(!this.hasFocus){
8270             return;
8271         }
8272          if (!opts.add) {
8273             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8274          }
8275         this.restrictHeight();
8276         this.selectedIndex = -1;
8277     },
8278
8279     // private
8280     onLoad : function(){
8281         
8282         this.hasQuery = false;
8283         
8284         if(!this.hasFocus){
8285             return;
8286         }
8287         
8288         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8289             this.loading.hide();
8290         }
8291         
8292         if(this.store.getCount() > 0){
8293             this.expand();
8294             this.restrictHeight();
8295             if(this.lastQuery == this.allQuery){
8296                 if(this.editable){
8297                     this.inputEl().dom.select();
8298                 }
8299                 if(!this.selectByValue(this.value, true)){
8300                     this.select(0, true);
8301                 }
8302             }else{
8303                 this.selectNext();
8304                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8305                     this.taTask.delay(this.typeAheadDelay);
8306                 }
8307             }
8308         }else{
8309             this.onEmptyResults();
8310         }
8311         
8312         //this.el.focus();
8313     },
8314     // private
8315     onLoadException : function()
8316     {
8317         this.hasQuery = false;
8318         
8319         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8320             this.loading.hide();
8321         }
8322         
8323         this.collapse();
8324         Roo.log(this.store.reader.jsonData);
8325         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8326             // fixme
8327             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8328         }
8329         
8330         
8331     },
8332     // private
8333     onTypeAhead : function(){
8334         if(this.store.getCount() > 0){
8335             var r = this.store.getAt(0);
8336             var newValue = r.data[this.displayField];
8337             var len = newValue.length;
8338             var selStart = this.getRawValue().length;
8339             
8340             if(selStart != len){
8341                 this.setRawValue(newValue);
8342                 this.selectText(selStart, newValue.length);
8343             }
8344         }
8345     },
8346
8347     // private
8348     onSelect : function(record, index){
8349         
8350         if(this.fireEvent('beforeselect', this, record, index) !== false){
8351         
8352             this.setFromData(index > -1 ? record.data : false);
8353             
8354             this.collapse();
8355             this.fireEvent('select', this, record, index);
8356         }
8357     },
8358
8359     /**
8360      * Returns the currently selected field value or empty string if no value is set.
8361      * @return {String} value The selected value
8362      */
8363     getValue : function(){
8364         
8365         if(this.multiple){
8366             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8367         }
8368         
8369         if(this.valueField){
8370             return typeof this.value != 'undefined' ? this.value : '';
8371         }else{
8372             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8373         }
8374     },
8375
8376     /**
8377      * Clears any text/value currently set in the field
8378      */
8379     clearValue : function(){
8380         if(this.hiddenField){
8381             this.hiddenField.dom.value = '';
8382         }
8383         this.value = '';
8384         this.setRawValue('');
8385         this.lastSelectionText = '';
8386         
8387     },
8388
8389     /**
8390      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8391      * will be displayed in the field.  If the value does not match the data value of an existing item,
8392      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8393      * Otherwise the field will be blank (although the value will still be set).
8394      * @param {String} value The value to match
8395      */
8396     setValue : function(v){
8397         if(this.multiple){
8398             this.syncValue();
8399             return;
8400         }
8401         
8402         var text = v;
8403         if(this.valueField){
8404             var r = this.findRecord(this.valueField, v);
8405             if(r){
8406                 text = r.data[this.displayField];
8407             }else if(this.valueNotFoundText !== undefined){
8408                 text = this.valueNotFoundText;
8409             }
8410         }
8411         this.lastSelectionText = text;
8412         if(this.hiddenField){
8413             this.hiddenField.dom.value = v;
8414         }
8415         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8416         this.value = v;
8417     },
8418     /**
8419      * @property {Object} the last set data for the element
8420      */
8421     
8422     lastData : false,
8423     /**
8424      * Sets the value of the field based on a object which is related to the record format for the store.
8425      * @param {Object} value the value to set as. or false on reset?
8426      */
8427     setFromData : function(o){
8428         
8429         if(this.multiple){
8430             this.addItem(o);
8431             return;
8432         }
8433             
8434         var dv = ''; // display value
8435         var vv = ''; // value value..
8436         this.lastData = o;
8437         if (this.displayField) {
8438             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8439         } else {
8440             // this is an error condition!!!
8441             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8442         }
8443         
8444         if(this.valueField){
8445             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8446         }
8447         
8448         if(this.hiddenField){
8449             this.hiddenField.dom.value = vv;
8450             
8451             this.lastSelectionText = dv;
8452             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8453             this.value = vv;
8454             return;
8455         }
8456         // no hidden field.. - we store the value in 'value', but still display
8457         // display field!!!!
8458         this.lastSelectionText = dv;
8459         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8460         this.value = vv;
8461         
8462         
8463     },
8464     // private
8465     reset : function(){
8466         // overridden so that last data is reset..
8467         this.setValue(this.originalValue);
8468         this.clearInvalid();
8469         this.lastData = false;
8470         if (this.view) {
8471             this.view.clearSelections();
8472         }
8473     },
8474     // private
8475     findRecord : function(prop, value){
8476         var record;
8477         if(this.store.getCount() > 0){
8478             this.store.each(function(r){
8479                 if(r.data[prop] == value){
8480                     record = r;
8481                     return false;
8482                 }
8483                 return true;
8484             });
8485         }
8486         return record;
8487     },
8488     
8489     getName: function()
8490     {
8491         // returns hidden if it's set..
8492         if (!this.rendered) {return ''};
8493         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8494         
8495     },
8496     // private
8497     onViewMove : function(e, t){
8498         this.inKeyMode = false;
8499     },
8500
8501     // private
8502     onViewOver : function(e, t){
8503         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8504             return;
8505         }
8506         var item = this.view.findItemFromChild(t);
8507         if(item){
8508             var index = this.view.indexOf(item);
8509             this.select(index, false);
8510         }
8511     },
8512
8513     // private
8514     onViewClick : function(doFocus)
8515     {
8516         var index = this.view.getSelectedIndexes()[0];
8517         var r = this.store.getAt(index);
8518         if(r){
8519             this.onSelect(r, index);
8520         }
8521         if(doFocus !== false && !this.blockFocus){
8522             this.inputEl().focus();
8523         }
8524     },
8525
8526     // private
8527     restrictHeight : function(){
8528         //this.innerList.dom.style.height = '';
8529         //var inner = this.innerList.dom;
8530         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8531         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8532         //this.list.beginUpdate();
8533         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8534         this.list.alignTo(this.inputEl(), this.listAlign);
8535         //this.list.endUpdate();
8536     },
8537
8538     // private
8539     onEmptyResults : function(){
8540         this.collapse();
8541     },
8542
8543     /**
8544      * Returns true if the dropdown list is expanded, else false.
8545      */
8546     isExpanded : function(){
8547         return this.list.isVisible();
8548     },
8549
8550     /**
8551      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8552      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8553      * @param {String} value The data value of the item to select
8554      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8555      * selected item if it is not currently in view (defaults to true)
8556      * @return {Boolean} True if the value matched an item in the list, else false
8557      */
8558     selectByValue : function(v, scrollIntoView){
8559         if(v !== undefined && v !== null){
8560             var r = this.findRecord(this.valueField || this.displayField, v);
8561             if(r){
8562                 this.select(this.store.indexOf(r), scrollIntoView);
8563                 return true;
8564             }
8565         }
8566         return false;
8567     },
8568
8569     /**
8570      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8571      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8572      * @param {Number} index The zero-based index of the list item to select
8573      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8574      * selected item if it is not currently in view (defaults to true)
8575      */
8576     select : function(index, scrollIntoView){
8577         this.selectedIndex = index;
8578         this.view.select(index);
8579         if(scrollIntoView !== false){
8580             var el = this.view.getNode(index);
8581             if(el){
8582                 //this.innerList.scrollChildIntoView(el, false);
8583                 
8584             }
8585         }
8586     },
8587
8588     // private
8589     selectNext : function(){
8590         var ct = this.store.getCount();
8591         if(ct > 0){
8592             if(this.selectedIndex == -1){
8593                 this.select(0);
8594             }else if(this.selectedIndex < ct-1){
8595                 this.select(this.selectedIndex+1);
8596             }
8597         }
8598     },
8599
8600     // private
8601     selectPrev : function(){
8602         var ct = this.store.getCount();
8603         if(ct > 0){
8604             if(this.selectedIndex == -1){
8605                 this.select(0);
8606             }else if(this.selectedIndex != 0){
8607                 this.select(this.selectedIndex-1);
8608             }
8609         }
8610     },
8611
8612     // private
8613     onKeyUp : function(e){
8614         if(this.editable !== false && !e.isSpecialKey()){
8615             this.lastKey = e.getKey();
8616             this.dqTask.delay(this.queryDelay);
8617         }
8618     },
8619
8620     // private
8621     validateBlur : function(){
8622         return !this.list || !this.list.isVisible();   
8623     },
8624
8625     // private
8626     initQuery : function(){
8627         this.doQuery(this.getRawValue());
8628     },
8629
8630     // private
8631     doForce : function(){
8632         if(this.el.dom.value.length > 0){
8633             this.el.dom.value =
8634                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8635              
8636         }
8637     },
8638
8639     /**
8640      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8641      * query allowing the query action to be canceled if needed.
8642      * @param {String} query The SQL query to execute
8643      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8644      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8645      * saved in the current store (defaults to false)
8646      */
8647     doQuery : function(q, forceAll){
8648         
8649         if(q === undefined || q === null){
8650             q = '';
8651         }
8652         var qe = {
8653             query: q,
8654             forceAll: forceAll,
8655             combo: this,
8656             cancel:false
8657         };
8658         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8659             return false;
8660         }
8661         q = qe.query;
8662         
8663         forceAll = qe.forceAll;
8664         if(forceAll === true || (q.length >= this.minChars)){
8665             
8666             this.hasQuery = true;
8667             
8668             if(this.lastQuery != q || this.alwaysQuery){
8669                 this.lastQuery = q;
8670                 if(this.mode == 'local'){
8671                     this.selectedIndex = -1;
8672                     if(forceAll){
8673                         this.store.clearFilter();
8674                     }else{
8675                         this.store.filter(this.displayField, q);
8676                     }
8677                     this.onLoad();
8678                 }else{
8679                     this.store.baseParams[this.queryParam] = q;
8680                     
8681                     var options = {params : this.getParams(q)};
8682                     
8683                     if(this.loadNext){
8684                         options.add = true;
8685                         options.params.start = this.page * this.pageSize;
8686                     }
8687                     
8688                     this.store.load(options);
8689                     this.expand();
8690                 }
8691             }else{
8692                 this.selectedIndex = -1;
8693                 this.onLoad();   
8694             }
8695         }
8696         
8697         this.loadNext = false;
8698     },
8699
8700     // private
8701     getParams : function(q){
8702         var p = {};
8703         //p[this.queryParam] = q;
8704         
8705         if(this.pageSize){
8706             p.start = 0;
8707             p.limit = this.pageSize;
8708         }
8709         return p;
8710     },
8711
8712     /**
8713      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8714      */
8715     collapse : function(){
8716         if(!this.isExpanded()){
8717             return;
8718         }
8719         
8720         this.list.hide();
8721         Roo.get(document).un('mousedown', this.collapseIf, this);
8722         Roo.get(document).un('mousewheel', this.collapseIf, this);
8723         if (!this.editable) {
8724             Roo.get(document).un('keydown', this.listKeyPress, this);
8725         }
8726         this.fireEvent('collapse', this);
8727     },
8728
8729     // private
8730     collapseIf : function(e){
8731         var in_combo  = e.within(this.el);
8732         var in_list =  e.within(this.list);
8733         
8734         if (in_combo || in_list) {
8735             //e.stopPropagation();
8736             return;
8737         }
8738
8739         this.collapse();
8740         
8741     },
8742
8743     /**
8744      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8745      */
8746     expand : function(){
8747        
8748         if(this.isExpanded() || !this.hasFocus){
8749             return;
8750         }
8751          Roo.log('expand');
8752         this.list.alignTo(this.inputEl(), this.listAlign);
8753         this.list.show();
8754         Roo.get(document).on('mousedown', this.collapseIf, this);
8755         Roo.get(document).on('mousewheel', this.collapseIf, this);
8756         if (!this.editable) {
8757             Roo.get(document).on('keydown', this.listKeyPress, this);
8758         }
8759         
8760         this.fireEvent('expand', this);
8761     },
8762
8763     // private
8764     // Implements the default empty TriggerField.onTriggerClick function
8765     onTriggerClick : function()
8766     {
8767         Roo.log('trigger click');
8768         
8769         if(this.disabled){
8770             return;
8771         }
8772         
8773         this.page = 0;
8774         this.loadNext = false;
8775         
8776         if(this.isExpanded()){
8777             this.collapse();
8778             if (!this.blockFocus) {
8779                 this.inputEl().focus();
8780             }
8781             
8782         }else {
8783             this.hasFocus = true;
8784             if(this.triggerAction == 'all') {
8785                 this.doQuery(this.allQuery, true);
8786             } else {
8787                 this.doQuery(this.getRawValue());
8788             }
8789             if (!this.blockFocus) {
8790                 this.inputEl().focus();
8791             }
8792         }
8793     },
8794     listKeyPress : function(e)
8795     {
8796         //Roo.log('listkeypress');
8797         // scroll to first matching element based on key pres..
8798         if (e.isSpecialKey()) {
8799             return false;
8800         }
8801         var k = String.fromCharCode(e.getKey()).toUpperCase();
8802         //Roo.log(k);
8803         var match  = false;
8804         var csel = this.view.getSelectedNodes();
8805         var cselitem = false;
8806         if (csel.length) {
8807             var ix = this.view.indexOf(csel[0]);
8808             cselitem  = this.store.getAt(ix);
8809             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8810                 cselitem = false;
8811             }
8812             
8813         }
8814         
8815         this.store.each(function(v) { 
8816             if (cselitem) {
8817                 // start at existing selection.
8818                 if (cselitem.id == v.id) {
8819                     cselitem = false;
8820                 }
8821                 return true;
8822             }
8823                 
8824             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8825                 match = this.store.indexOf(v);
8826                 return false;
8827             }
8828             return true;
8829         }, this);
8830         
8831         if (match === false) {
8832             return true; // no more action?
8833         }
8834         // scroll to?
8835         this.view.select(match);
8836         var sn = Roo.get(this.view.getSelectedNodes()[0])
8837         //sn.scrollIntoView(sn.dom.parentNode, false);
8838     },
8839     
8840     onViewScroll : function(e, t){
8841         
8842         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8843             return;
8844         }
8845         
8846         this.hasQuery = true;
8847         
8848         this.loading = this.list.select('.loading', true).first();
8849         
8850         if(this.loading === null){
8851             this.list.createChild({
8852                 tag: 'div',
8853                 cls: 'loading select2-more-results select2-active',
8854                 html: 'Loading more results...'
8855             })
8856             
8857             this.loading = this.list.select('.loading', true).first();
8858             
8859             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8860             
8861             this.loading.hide();
8862         }
8863         
8864         this.loading.show();
8865         
8866         var _combo = this;
8867         
8868         this.page++;
8869         this.loadNext = true;
8870         
8871         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8872         
8873         return;
8874     },
8875     
8876     addItem : function(o)
8877     {   
8878         var dv = ''; // display value
8879         
8880         if (this.displayField) {
8881             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8882         } else {
8883             // this is an error condition!!!
8884             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8885         }
8886         
8887         if(!dv.length){
8888             return;
8889         }
8890         
8891         var choice = this.choices.createChild({
8892             tag: 'li',
8893             cls: 'select2-search-choice',
8894             cn: [
8895                 {
8896                     tag: 'div',
8897                     html: dv
8898                 },
8899                 {
8900                     tag: 'a',
8901                     href: '#',
8902                     cls: 'select2-search-choice-close',
8903                     tabindex: '-1'
8904                 }
8905             ]
8906             
8907         }, this.searchField);
8908         
8909         var close = choice.select('a.select2-search-choice-close', true).first()
8910         
8911         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8912         
8913         this.item.push(o);
8914         this.lastData = o;
8915         
8916         this.syncValue();
8917         
8918         this.inputEl().dom.value = '';
8919         
8920     },
8921     
8922     onRemoveItem : function(e, _self, o)
8923     {
8924         Roo.log('remove item');
8925         var index = this.item.indexOf(o.data) * 1;
8926         
8927         if( index < 0){
8928             Roo.log('not this item?!');
8929             return;
8930         }
8931         
8932         this.item.splice(index, 1);
8933         o.item.remove();
8934         
8935         this.syncValue();
8936         
8937         this.fireEvent('remove', this);
8938         
8939     },
8940     
8941     syncValue : function()
8942     {
8943         if(!this.item.length){
8944             this.clearValue();
8945             return;
8946         }
8947             
8948         var value = [];
8949         var _this = this;
8950         Roo.each(this.item, function(i){
8951             if(_this.valueField){
8952                 value.push(i[_this.valueField]);
8953                 return;
8954             }
8955
8956             value.push(i);
8957         });
8958
8959         this.value = value.join(',');
8960
8961         if(this.hiddenField){
8962             this.hiddenField.dom.value = this.value;
8963         }
8964     },
8965     
8966     clearItem : function()
8967     {
8968         if(!this.multiple){
8969             return;
8970         }
8971         
8972         this.item = [];
8973         
8974         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
8975            c.remove();
8976         });
8977         
8978         this.syncValue();
8979     }
8980     
8981     
8982
8983     /** 
8984     * @cfg {Boolean} grow 
8985     * @hide 
8986     */
8987     /** 
8988     * @cfg {Number} growMin 
8989     * @hide 
8990     */
8991     /** 
8992     * @cfg {Number} growMax 
8993     * @hide 
8994     */
8995     /**
8996      * @hide
8997      * @method autoSize
8998      */
8999 });
9000 /*
9001  * Based on:
9002  * Ext JS Library 1.1.1
9003  * Copyright(c) 2006-2007, Ext JS, LLC.
9004  *
9005  * Originally Released Under LGPL - original licence link has changed is not relivant.
9006  *
9007  * Fork - LGPL
9008  * <script type="text/javascript">
9009  */
9010
9011 /**
9012  * @class Roo.View
9013  * @extends Roo.util.Observable
9014  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9015  * This class also supports single and multi selection modes. <br>
9016  * Create a data model bound view:
9017  <pre><code>
9018  var store = new Roo.data.Store(...);
9019
9020  var view = new Roo.View({
9021     el : "my-element",
9022     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9023  
9024     singleSelect: true,
9025     selectedClass: "ydataview-selected",
9026     store: store
9027  });
9028
9029  // listen for node click?
9030  view.on("click", function(vw, index, node, e){
9031  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9032  });
9033
9034  // load XML data
9035  dataModel.load("foobar.xml");
9036  </code></pre>
9037  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9038  * <br><br>
9039  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9040  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9041  * 
9042  * Note: old style constructor is still suported (container, template, config)
9043  * 
9044  * @constructor
9045  * Create a new View
9046  * @param {Object} config The config object
9047  * 
9048  */
9049 Roo.View = function(config, depreciated_tpl, depreciated_config){
9050     
9051     if (typeof(depreciated_tpl) == 'undefined') {
9052         // new way.. - universal constructor.
9053         Roo.apply(this, config);
9054         this.el  = Roo.get(this.el);
9055     } else {
9056         // old format..
9057         this.el  = Roo.get(config);
9058         this.tpl = depreciated_tpl;
9059         Roo.apply(this, depreciated_config);
9060     }
9061     this.wrapEl  = this.el.wrap().wrap();
9062     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9063     
9064     
9065     if(typeof(this.tpl) == "string"){
9066         this.tpl = new Roo.Template(this.tpl);
9067     } else {
9068         // support xtype ctors..
9069         this.tpl = new Roo.factory(this.tpl, Roo);
9070     }
9071     
9072     
9073     this.tpl.compile();
9074    
9075   
9076     
9077      
9078     /** @private */
9079     this.addEvents({
9080         /**
9081          * @event beforeclick
9082          * Fires before a click is processed. Returns false to cancel the default action.
9083          * @param {Roo.View} this
9084          * @param {Number} index The index of the target node
9085          * @param {HTMLElement} node The target node
9086          * @param {Roo.EventObject} e The raw event object
9087          */
9088             "beforeclick" : true,
9089         /**
9090          * @event click
9091          * Fires when a template node is clicked.
9092          * @param {Roo.View} this
9093          * @param {Number} index The index of the target node
9094          * @param {HTMLElement} node The target node
9095          * @param {Roo.EventObject} e The raw event object
9096          */
9097             "click" : true,
9098         /**
9099          * @event dblclick
9100          * Fires when a template node is double clicked.
9101          * @param {Roo.View} this
9102          * @param {Number} index The index of the target node
9103          * @param {HTMLElement} node The target node
9104          * @param {Roo.EventObject} e The raw event object
9105          */
9106             "dblclick" : true,
9107         /**
9108          * @event contextmenu
9109          * Fires when a template node is right clicked.
9110          * @param {Roo.View} this
9111          * @param {Number} index The index of the target node
9112          * @param {HTMLElement} node The target node
9113          * @param {Roo.EventObject} e The raw event object
9114          */
9115             "contextmenu" : true,
9116         /**
9117          * @event selectionchange
9118          * Fires when the selected nodes change.
9119          * @param {Roo.View} this
9120          * @param {Array} selections Array of the selected nodes
9121          */
9122             "selectionchange" : true,
9123     
9124         /**
9125          * @event beforeselect
9126          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9127          * @param {Roo.View} this
9128          * @param {HTMLElement} node The node to be selected
9129          * @param {Array} selections Array of currently selected nodes
9130          */
9131             "beforeselect" : true,
9132         /**
9133          * @event preparedata
9134          * Fires on every row to render, to allow you to change the data.
9135          * @param {Roo.View} this
9136          * @param {Object} data to be rendered (change this)
9137          */
9138           "preparedata" : true
9139           
9140           
9141         });
9142
9143
9144
9145     this.el.on({
9146         "click": this.onClick,
9147         "dblclick": this.onDblClick,
9148         "contextmenu": this.onContextMenu,
9149         scope:this
9150     });
9151
9152     this.selections = [];
9153     this.nodes = [];
9154     this.cmp = new Roo.CompositeElementLite([]);
9155     if(this.store){
9156         this.store = Roo.factory(this.store, Roo.data);
9157         this.setStore(this.store, true);
9158     }
9159     
9160     if ( this.footer && this.footer.xtype) {
9161            
9162          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9163         
9164         this.footer.dataSource = this.store
9165         this.footer.container = fctr;
9166         this.footer = Roo.factory(this.footer, Roo);
9167         fctr.insertFirst(this.el);
9168         
9169         // this is a bit insane - as the paging toolbar seems to detach the el..
9170 //        dom.parentNode.parentNode.parentNode
9171          // they get detached?
9172     }
9173     
9174     
9175     Roo.View.superclass.constructor.call(this);
9176     
9177     
9178 };
9179
9180 Roo.extend(Roo.View, Roo.util.Observable, {
9181     
9182      /**
9183      * @cfg {Roo.data.Store} store Data store to load data from.
9184      */
9185     store : false,
9186     
9187     /**
9188      * @cfg {String|Roo.Element} el The container element.
9189      */
9190     el : '',
9191     
9192     /**
9193      * @cfg {String|Roo.Template} tpl The template used by this View 
9194      */
9195     tpl : false,
9196     /**
9197      * @cfg {String} dataName the named area of the template to use as the data area
9198      *                          Works with domtemplates roo-name="name"
9199      */
9200     dataName: false,
9201     /**
9202      * @cfg {String} selectedClass The css class to add to selected nodes
9203      */
9204     selectedClass : "x-view-selected",
9205      /**
9206      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9207      */
9208     emptyText : "",
9209     
9210     /**
9211      * @cfg {String} text to display on mask (default Loading)
9212      */
9213     mask : false,
9214     /**
9215      * @cfg {Boolean} multiSelect Allow multiple selection
9216      */
9217     multiSelect : false,
9218     /**
9219      * @cfg {Boolean} singleSelect Allow single selection
9220      */
9221     singleSelect:  false,
9222     
9223     /**
9224      * @cfg {Boolean} toggleSelect - selecting 
9225      */
9226     toggleSelect : false,
9227     
9228     /**
9229      * Returns the element this view is bound to.
9230      * @return {Roo.Element}
9231      */
9232     getEl : function(){
9233         return this.wrapEl;
9234     },
9235     
9236     
9237
9238     /**
9239      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9240      */
9241     refresh : function(){
9242         Roo.log('refresh');
9243         var t = this.tpl;
9244         
9245         // if we are using something like 'domtemplate', then
9246         // the what gets used is:
9247         // t.applySubtemplate(NAME, data, wrapping data..)
9248         // the outer template then get' applied with
9249         //     the store 'extra data'
9250         // and the body get's added to the
9251         //      roo-name="data" node?
9252         //      <span class='roo-tpl-{name}'></span> ?????
9253         
9254         
9255         
9256         this.clearSelections();
9257         this.el.update("");
9258         var html = [];
9259         var records = this.store.getRange();
9260         if(records.length < 1) {
9261             
9262             // is this valid??  = should it render a template??
9263             
9264             this.el.update(this.emptyText);
9265             return;
9266         }
9267         var el = this.el;
9268         if (this.dataName) {
9269             this.el.update(t.apply(this.store.meta)); //????
9270             el = this.el.child('.roo-tpl-' + this.dataName);
9271         }
9272         
9273         for(var i = 0, len = records.length; i < len; i++){
9274             var data = this.prepareData(records[i].data, i, records[i]);
9275             this.fireEvent("preparedata", this, data, i, records[i]);
9276             html[html.length] = Roo.util.Format.trim(
9277                 this.dataName ?
9278                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9279                     t.apply(data)
9280             );
9281         }
9282         
9283         
9284         
9285         el.update(html.join(""));
9286         this.nodes = el.dom.childNodes;
9287         this.updateIndexes(0);
9288     },
9289     
9290
9291     /**
9292      * Function to override to reformat the data that is sent to
9293      * the template for each node.
9294      * DEPRICATED - use the preparedata event handler.
9295      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9296      * a JSON object for an UpdateManager bound view).
9297      */
9298     prepareData : function(data, index, record)
9299     {
9300         this.fireEvent("preparedata", this, data, index, record);
9301         return data;
9302     },
9303
9304     onUpdate : function(ds, record){
9305          Roo.log('on update');   
9306         this.clearSelections();
9307         var index = this.store.indexOf(record);
9308         var n = this.nodes[index];
9309         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9310         n.parentNode.removeChild(n);
9311         this.updateIndexes(index, index);
9312     },
9313
9314     
9315     
9316 // --------- FIXME     
9317     onAdd : function(ds, records, index)
9318     {
9319         Roo.log(['on Add', ds, records, index] );        
9320         this.clearSelections();
9321         if(this.nodes.length == 0){
9322             this.refresh();
9323             return;
9324         }
9325         var n = this.nodes[index];
9326         for(var i = 0, len = records.length; i < len; i++){
9327             var d = this.prepareData(records[i].data, i, records[i]);
9328             if(n){
9329                 this.tpl.insertBefore(n, d);
9330             }else{
9331                 
9332                 this.tpl.append(this.el, d);
9333             }
9334         }
9335         this.updateIndexes(index);
9336     },
9337
9338     onRemove : function(ds, record, index){
9339         Roo.log('onRemove');
9340         this.clearSelections();
9341         var el = this.dataName  ?
9342             this.el.child('.roo-tpl-' + this.dataName) :
9343             this.el; 
9344         
9345         el.dom.removeChild(this.nodes[index]);
9346         this.updateIndexes(index);
9347     },
9348
9349     /**
9350      * Refresh an individual node.
9351      * @param {Number} index
9352      */
9353     refreshNode : function(index){
9354         this.onUpdate(this.store, this.store.getAt(index));
9355     },
9356
9357     updateIndexes : function(startIndex, endIndex){
9358         var ns = this.nodes;
9359         startIndex = startIndex || 0;
9360         endIndex = endIndex || ns.length - 1;
9361         for(var i = startIndex; i <= endIndex; i++){
9362             ns[i].nodeIndex = i;
9363         }
9364     },
9365
9366     /**
9367      * Changes the data store this view uses and refresh the view.
9368      * @param {Store} store
9369      */
9370     setStore : function(store, initial){
9371         if(!initial && this.store){
9372             this.store.un("datachanged", this.refresh);
9373             this.store.un("add", this.onAdd);
9374             this.store.un("remove", this.onRemove);
9375             this.store.un("update", this.onUpdate);
9376             this.store.un("clear", this.refresh);
9377             this.store.un("beforeload", this.onBeforeLoad);
9378             this.store.un("load", this.onLoad);
9379             this.store.un("loadexception", this.onLoad);
9380         }
9381         if(store){
9382           
9383             store.on("datachanged", this.refresh, this);
9384             store.on("add", this.onAdd, this);
9385             store.on("remove", this.onRemove, this);
9386             store.on("update", this.onUpdate, this);
9387             store.on("clear", this.refresh, this);
9388             store.on("beforeload", this.onBeforeLoad, this);
9389             store.on("load", this.onLoad, this);
9390             store.on("loadexception", this.onLoad, this);
9391         }
9392         
9393         if(store){
9394             this.refresh();
9395         }
9396     },
9397     /**
9398      * onbeforeLoad - masks the loading area.
9399      *
9400      */
9401     onBeforeLoad : function(store,opts)
9402     {
9403          Roo.log('onBeforeLoad');   
9404         if (!opts.add) {
9405             this.el.update("");
9406         }
9407         this.el.mask(this.mask ? this.mask : "Loading" ); 
9408     },
9409     onLoad : function ()
9410     {
9411         this.el.unmask();
9412     },
9413     
9414
9415     /**
9416      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9417      * @param {HTMLElement} node
9418      * @return {HTMLElement} The template node
9419      */
9420     findItemFromChild : function(node){
9421         var el = this.dataName  ?
9422             this.el.child('.roo-tpl-' + this.dataName,true) :
9423             this.el.dom; 
9424         
9425         if(!node || node.parentNode == el){
9426                     return node;
9427             }
9428             var p = node.parentNode;
9429             while(p && p != el){
9430             if(p.parentNode == el){
9431                 return p;
9432             }
9433             p = p.parentNode;
9434         }
9435             return null;
9436     },
9437
9438     /** @ignore */
9439     onClick : function(e){
9440         var item = this.findItemFromChild(e.getTarget());
9441         if(item){
9442             var index = this.indexOf(item);
9443             if(this.onItemClick(item, index, e) !== false){
9444                 this.fireEvent("click", this, index, item, e);
9445             }
9446         }else{
9447             this.clearSelections();
9448         }
9449     },
9450
9451     /** @ignore */
9452     onContextMenu : function(e){
9453         var item = this.findItemFromChild(e.getTarget());
9454         if(item){
9455             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9456         }
9457     },
9458
9459     /** @ignore */
9460     onDblClick : function(e){
9461         var item = this.findItemFromChild(e.getTarget());
9462         if(item){
9463             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9464         }
9465     },
9466
9467     onItemClick : function(item, index, e)
9468     {
9469         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9470             return false;
9471         }
9472         if (this.toggleSelect) {
9473             var m = this.isSelected(item) ? 'unselect' : 'select';
9474             Roo.log(m);
9475             var _t = this;
9476             _t[m](item, true, false);
9477             return true;
9478         }
9479         if(this.multiSelect || this.singleSelect){
9480             if(this.multiSelect && e.shiftKey && this.lastSelection){
9481                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9482             }else{
9483                 this.select(item, this.multiSelect && e.ctrlKey);
9484                 this.lastSelection = item;
9485             }
9486             e.preventDefault();
9487         }
9488         return true;
9489     },
9490
9491     /**
9492      * Get the number of selected nodes.
9493      * @return {Number}
9494      */
9495     getSelectionCount : function(){
9496         return this.selections.length;
9497     },
9498
9499     /**
9500      * Get the currently selected nodes.
9501      * @return {Array} An array of HTMLElements
9502      */
9503     getSelectedNodes : function(){
9504         return this.selections;
9505     },
9506
9507     /**
9508      * Get the indexes of the selected nodes.
9509      * @return {Array}
9510      */
9511     getSelectedIndexes : function(){
9512         var indexes = [], s = this.selections;
9513         for(var i = 0, len = s.length; i < len; i++){
9514             indexes.push(s[i].nodeIndex);
9515         }
9516         return indexes;
9517     },
9518
9519     /**
9520      * Clear all selections
9521      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9522      */
9523     clearSelections : function(suppressEvent){
9524         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9525             this.cmp.elements = this.selections;
9526             this.cmp.removeClass(this.selectedClass);
9527             this.selections = [];
9528             if(!suppressEvent){
9529                 this.fireEvent("selectionchange", this, this.selections);
9530             }
9531         }
9532     },
9533
9534     /**
9535      * Returns true if the passed node is selected
9536      * @param {HTMLElement/Number} node The node or node index
9537      * @return {Boolean}
9538      */
9539     isSelected : function(node){
9540         var s = this.selections;
9541         if(s.length < 1){
9542             return false;
9543         }
9544         node = this.getNode(node);
9545         return s.indexOf(node) !== -1;
9546     },
9547
9548     /**
9549      * Selects nodes.
9550      * @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
9551      * @param {Boolean} keepExisting (optional) true to keep existing selections
9552      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9553      */
9554     select : function(nodeInfo, keepExisting, suppressEvent){
9555         if(nodeInfo instanceof Array){
9556             if(!keepExisting){
9557                 this.clearSelections(true);
9558             }
9559             for(var i = 0, len = nodeInfo.length; i < len; i++){
9560                 this.select(nodeInfo[i], true, true);
9561             }
9562             return;
9563         } 
9564         var node = this.getNode(nodeInfo);
9565         if(!node || this.isSelected(node)){
9566             return; // already selected.
9567         }
9568         if(!keepExisting){
9569             this.clearSelections(true);
9570         }
9571         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9572             Roo.fly(node).addClass(this.selectedClass);
9573             this.selections.push(node);
9574             if(!suppressEvent){
9575                 this.fireEvent("selectionchange", this, this.selections);
9576             }
9577         }
9578         
9579         
9580     },
9581       /**
9582      * Unselects nodes.
9583      * @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
9584      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9585      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9586      */
9587     unselect : function(nodeInfo, keepExisting, suppressEvent)
9588     {
9589         if(nodeInfo instanceof Array){
9590             Roo.each(this.selections, function(s) {
9591                 this.unselect(s, nodeInfo);
9592             }, this);
9593             return;
9594         }
9595         var node = this.getNode(nodeInfo);
9596         if(!node || !this.isSelected(node)){
9597             Roo.log("not selected");
9598             return; // not selected.
9599         }
9600         // fireevent???
9601         var ns = [];
9602         Roo.each(this.selections, function(s) {
9603             if (s == node ) {
9604                 Roo.fly(node).removeClass(this.selectedClass);
9605
9606                 return;
9607             }
9608             ns.push(s);
9609         },this);
9610         
9611         this.selections= ns;
9612         this.fireEvent("selectionchange", this, this.selections);
9613     },
9614
9615     /**
9616      * Gets a template node.
9617      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9618      * @return {HTMLElement} The node or null if it wasn't found
9619      */
9620     getNode : function(nodeInfo){
9621         if(typeof nodeInfo == "string"){
9622             return document.getElementById(nodeInfo);
9623         }else if(typeof nodeInfo == "number"){
9624             return this.nodes[nodeInfo];
9625         }
9626         return nodeInfo;
9627     },
9628
9629     /**
9630      * Gets a range template nodes.
9631      * @param {Number} startIndex
9632      * @param {Number} endIndex
9633      * @return {Array} An array of nodes
9634      */
9635     getNodes : function(start, end){
9636         var ns = this.nodes;
9637         start = start || 0;
9638         end = typeof end == "undefined" ? ns.length - 1 : end;
9639         var nodes = [];
9640         if(start <= end){
9641             for(var i = start; i <= end; i++){
9642                 nodes.push(ns[i]);
9643             }
9644         } else{
9645             for(var i = start; i >= end; i--){
9646                 nodes.push(ns[i]);
9647             }
9648         }
9649         return nodes;
9650     },
9651
9652     /**
9653      * Finds the index of the passed node
9654      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9655      * @return {Number} The index of the node or -1
9656      */
9657     indexOf : function(node){
9658         node = this.getNode(node);
9659         if(typeof node.nodeIndex == "number"){
9660             return node.nodeIndex;
9661         }
9662         var ns = this.nodes;
9663         for(var i = 0, len = ns.length; i < len; i++){
9664             if(ns[i] == node){
9665                 return i;
9666             }
9667         }
9668         return -1;
9669     }
9670 });
9671 /*
9672  * - LGPL
9673  *
9674  * based on jquery fullcalendar
9675  * 
9676  */
9677
9678 Roo.bootstrap = Roo.bootstrap || {};
9679 /**
9680  * @class Roo.bootstrap.Calendar
9681  * @extends Roo.bootstrap.Component
9682  * Bootstrap Calendar class
9683  * @cfg {Boolean} loadMask (true|false) default false
9684     
9685  * @constructor
9686  * Create a new Container
9687  * @param {Object} config The config object
9688  */
9689
9690
9691
9692 Roo.bootstrap.Calendar = function(config){
9693     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9694      this.addEvents({
9695         /**
9696              * @event select
9697              * Fires when a date is selected
9698              * @param {DatePicker} this
9699              * @param {Date} date The selected date
9700              */
9701         'select': true,
9702         /**
9703              * @event monthchange
9704              * Fires when the displayed month changes 
9705              * @param {DatePicker} this
9706              * @param {Date} date The selected month
9707              */
9708         'monthchange': true,
9709         /**
9710              * @event evententer
9711              * Fires when mouse over an event
9712              * @param {Calendar} this
9713              * @param {event} Event
9714              */
9715         'evententer': true,
9716         /**
9717              * @event eventleave
9718              * Fires when the mouse leaves an
9719              * @param {Calendar} this
9720              * @param {event}
9721              */
9722         'eventleave': true,
9723         /**
9724              * @event eventclick
9725              * Fires when the mouse click an
9726              * @param {Calendar} this
9727              * @param {event}
9728              */
9729         'eventclick': true
9730         
9731     });
9732
9733 };
9734
9735 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9736     
9737      /**
9738      * @cfg {Number} startDay
9739      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9740      */
9741     startDay : 0,
9742     
9743     loadMask : false,
9744       
9745     getAutoCreate : function(){
9746         
9747         
9748         var fc_button = function(name, corner, style, content ) {
9749             return Roo.apply({},{
9750                 tag : 'span',
9751                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9752                          (corner.length ?
9753                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9754                             ''
9755                         ),
9756                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9757                 unselectable: 'on'
9758             });
9759         };
9760         
9761         var header = {
9762             tag : 'table',
9763             cls : 'fc-header',
9764             style : 'width:100%',
9765             cn : [
9766                 {
9767                     tag: 'tr',
9768                     cn : [
9769                         {
9770                             tag : 'td',
9771                             cls : 'fc-header-left',
9772                             cn : [
9773                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9774                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9775                                 { tag: 'span', cls: 'fc-header-space' },
9776                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9777                                 
9778                                 
9779                             ]
9780                         },
9781                         
9782                         {
9783                             tag : 'td',
9784                             cls : 'fc-header-center',
9785                             cn : [
9786                                 {
9787                                     tag: 'span',
9788                                     cls: 'fc-header-title',
9789                                     cn : {
9790                                         tag: 'H2',
9791                                         html : 'month / year'
9792                                     }
9793                                 }
9794                                 
9795                             ]
9796                         },
9797                         {
9798                             tag : 'td',
9799                             cls : 'fc-header-right',
9800                             cn : [
9801                           /*      fc_button('month', 'left', '', 'month' ),
9802                                 fc_button('week', '', '', 'week' ),
9803                                 fc_button('day', 'right', '', 'day' )
9804                             */    
9805                                 
9806                             ]
9807                         }
9808                         
9809                     ]
9810                 }
9811             ]
9812         };
9813         
9814        
9815         var cal_heads = function() {
9816             var ret = [];
9817             // fixme - handle this.
9818             
9819             for (var i =0; i < Date.dayNames.length; i++) {
9820                 var d = Date.dayNames[i];
9821                 ret.push({
9822                     tag: 'th',
9823                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9824                     html : d.substring(0,3)
9825                 });
9826                 
9827             }
9828             ret[0].cls += ' fc-first';
9829             ret[6].cls += ' fc-last';
9830             return ret;
9831         };
9832         var cal_cell = function(n) {
9833             return  {
9834                 tag: 'td',
9835                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9836                 cn : [
9837                     {
9838                         cn : [
9839                             {
9840                                 cls: 'fc-day-number',
9841                                 html: 'D'
9842                             },
9843                             {
9844                                 cls: 'fc-day-content',
9845                              
9846                                 cn : [
9847                                      {
9848                                         style: 'position: relative;' // height: 17px;
9849                                     }
9850                                 ]
9851                             }
9852                             
9853                             
9854                         ]
9855                     }
9856                 ]
9857                 
9858             }
9859         };
9860         var cal_rows = function() {
9861             
9862             var ret = []
9863             for (var r = 0; r < 6; r++) {
9864                 var row= {
9865                     tag : 'tr',
9866                     cls : 'fc-week',
9867                     cn : []
9868                 };
9869                 
9870                 for (var i =0; i < Date.dayNames.length; i++) {
9871                     var d = Date.dayNames[i];
9872                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9873
9874                 }
9875                 row.cn[0].cls+=' fc-first';
9876                 row.cn[0].cn[0].style = 'min-height:90px';
9877                 row.cn[6].cls+=' fc-last';
9878                 ret.push(row);
9879                 
9880             }
9881             ret[0].cls += ' fc-first';
9882             ret[4].cls += ' fc-prev-last';
9883             ret[5].cls += ' fc-last';
9884             return ret;
9885             
9886         };
9887         
9888         var cal_table = {
9889             tag: 'table',
9890             cls: 'fc-border-separate',
9891             style : 'width:100%',
9892             cellspacing  : 0,
9893             cn : [
9894                 { 
9895                     tag: 'thead',
9896                     cn : [
9897                         { 
9898                             tag: 'tr',
9899                             cls : 'fc-first fc-last',
9900                             cn : cal_heads()
9901                         }
9902                     ]
9903                 },
9904                 { 
9905                     tag: 'tbody',
9906                     cn : cal_rows()
9907                 }
9908                   
9909             ]
9910         };
9911          
9912          var cfg = {
9913             cls : 'fc fc-ltr',
9914             cn : [
9915                 header,
9916                 {
9917                     cls : 'fc-content',
9918                     style : "position: relative;",
9919                     cn : [
9920                         {
9921                             cls : 'fc-view fc-view-month fc-grid',
9922                             style : 'position: relative',
9923                             unselectable : 'on',
9924                             cn : [
9925                                 {
9926                                     cls : 'fc-event-container',
9927                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9928                                 },
9929                                 cal_table
9930                             ]
9931                         }
9932                     ]
9933     
9934                 }
9935            ] 
9936             
9937         };
9938         
9939          
9940         
9941         return cfg;
9942     },
9943     
9944     
9945     initEvents : function()
9946     {
9947         if(!this.store){
9948             throw "can not find store for calendar";
9949         }
9950         
9951         var mark = {
9952             tag: "div",
9953             cls:"x-dlg-mask",
9954             style: "text-align:center",
9955             cn: [
9956                 {
9957                     tag: "div",
9958                     style: "background-color:white;width:50%;margin:250 auto",
9959                     cn: [
9960                         {
9961                             tag: "img",
9962                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9963                         },
9964                         {
9965                             tag: "span",
9966                             html: "Loading"
9967                         }
9968                         
9969                     ]
9970                 }
9971             ]
9972         }
9973         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9974         
9975         var size = this.el.select('.fc-content', true).first().getSize();
9976         this.maskEl.setSize(size.width, size.height);
9977         this.maskEl.enableDisplayMode("block");
9978         if(!this.loadMask){
9979             this.maskEl.hide();
9980         }
9981         
9982         this.store = Roo.factory(this.store, Roo.data);
9983         this.store.on('load', this.onLoad, this);
9984         this.store.on('beforeload', this.onBeforeLoad, this);
9985         
9986         this.resize();
9987         
9988         this.cells = this.el.select('.fc-day',true);
9989         //Roo.log(this.cells);
9990         this.textNodes = this.el.query('.fc-day-number');
9991         this.cells.addClassOnOver('fc-state-hover');
9992         
9993         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
9994         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
9995         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
9996         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
9997         
9998         this.on('monthchange', this.onMonthChange, this);
9999         
10000         this.update(new Date().clearTime());
10001     },
10002     
10003     resize : function() {
10004         var sz  = this.el.getSize();
10005         
10006         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10007         this.el.select('.fc-day-content div',true).setHeight(34);
10008     },
10009     
10010     
10011     // private
10012     showPrevMonth : function(e){
10013         this.update(this.activeDate.add("mo", -1));
10014     },
10015     showToday : function(e){
10016         this.update(new Date().clearTime());
10017     },
10018     // private
10019     showNextMonth : function(e){
10020         this.update(this.activeDate.add("mo", 1));
10021     },
10022
10023     // private
10024     showPrevYear : function(){
10025         this.update(this.activeDate.add("y", -1));
10026     },
10027
10028     // private
10029     showNextYear : function(){
10030         this.update(this.activeDate.add("y", 1));
10031     },
10032
10033     
10034    // private
10035     update : function(date)
10036     {
10037         var vd = this.activeDate;
10038         this.activeDate = date;
10039 //        if(vd && this.el){
10040 //            var t = date.getTime();
10041 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10042 //                Roo.log('using add remove');
10043 //                
10044 //                this.fireEvent('monthchange', this, date);
10045 //                
10046 //                this.cells.removeClass("fc-state-highlight");
10047 //                this.cells.each(function(c){
10048 //                   if(c.dateValue == t){
10049 //                       c.addClass("fc-state-highlight");
10050 //                       setTimeout(function(){
10051 //                            try{c.dom.firstChild.focus();}catch(e){}
10052 //                       }, 50);
10053 //                       return false;
10054 //                   }
10055 //                   return true;
10056 //                });
10057 //                return;
10058 //            }
10059 //        }
10060         
10061         var days = date.getDaysInMonth();
10062         
10063         var firstOfMonth = date.getFirstDateOfMonth();
10064         var startingPos = firstOfMonth.getDay()-this.startDay;
10065         
10066         if(startingPos < this.startDay){
10067             startingPos += 7;
10068         }
10069         
10070         var pm = date.add(Date.MONTH, -1);
10071         var prevStart = pm.getDaysInMonth()-startingPos;
10072 //        
10073         this.cells = this.el.select('.fc-day',true);
10074         this.textNodes = this.el.query('.fc-day-number');
10075         this.cells.addClassOnOver('fc-state-hover');
10076         
10077         var cells = this.cells.elements;
10078         var textEls = this.textNodes;
10079         
10080         Roo.each(cells, function(cell){
10081             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10082         });
10083         
10084         days += startingPos;
10085
10086         // convert everything to numbers so it's fast
10087         var day = 86400000;
10088         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10089         //Roo.log(d);
10090         //Roo.log(pm);
10091         //Roo.log(prevStart);
10092         
10093         var today = new Date().clearTime().getTime();
10094         var sel = date.clearTime().getTime();
10095         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10096         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10097         var ddMatch = this.disabledDatesRE;
10098         var ddText = this.disabledDatesText;
10099         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10100         var ddaysText = this.disabledDaysText;
10101         var format = this.format;
10102         
10103         var setCellClass = function(cal, cell){
10104             
10105             //Roo.log('set Cell Class');
10106             cell.title = "";
10107             var t = d.getTime();
10108             
10109             //Roo.log(d);
10110             
10111             cell.dateValue = t;
10112             if(t == today){
10113                 cell.className += " fc-today";
10114                 cell.className += " fc-state-highlight";
10115                 cell.title = cal.todayText;
10116             }
10117             if(t == sel){
10118                 // disable highlight in other month..
10119                 //cell.className += " fc-state-highlight";
10120                 
10121             }
10122             // disabling
10123             if(t < min) {
10124                 cell.className = " fc-state-disabled";
10125                 cell.title = cal.minText;
10126                 return;
10127             }
10128             if(t > max) {
10129                 cell.className = " fc-state-disabled";
10130                 cell.title = cal.maxText;
10131                 return;
10132             }
10133             if(ddays){
10134                 if(ddays.indexOf(d.getDay()) != -1){
10135                     cell.title = ddaysText;
10136                     cell.className = " fc-state-disabled";
10137                 }
10138             }
10139             if(ddMatch && format){
10140                 var fvalue = d.dateFormat(format);
10141                 if(ddMatch.test(fvalue)){
10142                     cell.title = ddText.replace("%0", fvalue);
10143                     cell.className = " fc-state-disabled";
10144                 }
10145             }
10146             
10147             if (!cell.initialClassName) {
10148                 cell.initialClassName = cell.dom.className;
10149             }
10150             
10151             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10152         };
10153
10154         var i = 0;
10155         
10156         for(; i < startingPos; i++) {
10157             textEls[i].innerHTML = (++prevStart);
10158             d.setDate(d.getDate()+1);
10159             
10160             cells[i].className = "fc-past fc-other-month";
10161             setCellClass(this, cells[i]);
10162         }
10163         
10164         var intDay = 0;
10165         
10166         for(; i < days; i++){
10167             intDay = i - startingPos + 1;
10168             textEls[i].innerHTML = (intDay);
10169             d.setDate(d.getDate()+1);
10170             
10171             cells[i].className = ''; // "x-date-active";
10172             setCellClass(this, cells[i]);
10173         }
10174         var extraDays = 0;
10175         
10176         for(; i < 42; i++) {
10177             textEls[i].innerHTML = (++extraDays);
10178             d.setDate(d.getDate()+1);
10179             
10180             cells[i].className = "fc-future fc-other-month";
10181             setCellClass(this, cells[i]);
10182         }
10183         
10184         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10185         
10186         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10187         
10188         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10189         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10190         
10191         if(totalRows != 6){
10192             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10193             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10194         }
10195         
10196         this.fireEvent('monthchange', this, date);
10197         
10198         
10199         /*
10200         if(!this.internalRender){
10201             var main = this.el.dom.firstChild;
10202             var w = main.offsetWidth;
10203             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10204             Roo.fly(main).setWidth(w);
10205             this.internalRender = true;
10206             // opera does not respect the auto grow header center column
10207             // then, after it gets a width opera refuses to recalculate
10208             // without a second pass
10209             if(Roo.isOpera && !this.secondPass){
10210                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10211                 this.secondPass = true;
10212                 this.update.defer(10, this, [date]);
10213             }
10214         }
10215         */
10216         
10217     },
10218     
10219     findCell : function(dt) {
10220         dt = dt.clearTime().getTime();
10221         var ret = false;
10222         this.cells.each(function(c){
10223             //Roo.log("check " +c.dateValue + '?=' + dt);
10224             if(c.dateValue == dt){
10225                 ret = c;
10226                 return false;
10227             }
10228             return true;
10229         });
10230         
10231         return ret;
10232     },
10233     
10234     findCells : function(ev) {
10235         var s = ev.start.clone().clearTime().getTime();
10236        // Roo.log(s);
10237         var e= ev.end.clone().clearTime().getTime();
10238        // Roo.log(e);
10239         var ret = [];
10240         this.cells.each(function(c){
10241              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10242             
10243             if(c.dateValue > e){
10244                 return ;
10245             }
10246             if(c.dateValue < s){
10247                 return ;
10248             }
10249             ret.push(c);
10250         });
10251         
10252         return ret;    
10253     },
10254     
10255     findBestRow: function(cells)
10256     {
10257         var ret = 0;
10258         
10259         for (var i =0 ; i < cells.length;i++) {
10260             ret  = Math.max(cells[i].rows || 0,ret);
10261         }
10262         return ret;
10263         
10264     },
10265     
10266     
10267     addItem : function(ev)
10268     {
10269         // look for vertical location slot in
10270         var cells = this.findCells(ev);
10271         
10272         ev.row = this.findBestRow(cells);
10273         
10274         // work out the location.
10275         
10276         var crow = false;
10277         var rows = [];
10278         for(var i =0; i < cells.length; i++) {
10279             if (!crow) {
10280                 crow = {
10281                     start : cells[i],
10282                     end :  cells[i]
10283                 };
10284                 continue;
10285             }
10286             if (crow.start.getY() == cells[i].getY()) {
10287                 // on same row.
10288                 crow.end = cells[i];
10289                 continue;
10290             }
10291             // different row.
10292             rows.push(crow);
10293             crow = {
10294                 start: cells[i],
10295                 end : cells[i]
10296             };
10297             
10298         }
10299         
10300         rows.push(crow);
10301         ev.els = [];
10302         ev.rows = rows;
10303         ev.cells = cells;
10304         for (var i = 0; i < cells.length;i++) {
10305             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10306             
10307         }
10308         
10309         this.calevents.push(ev);
10310     },
10311     
10312     clearEvents: function() {
10313         
10314         if(!this.calevents){
10315             return;
10316         }
10317         
10318         Roo.each(this.cells.elements, function(c){
10319             c.rows = 0;
10320         });
10321         
10322         Roo.each(this.calevents, function(e) {
10323             Roo.each(e.els, function(el) {
10324                 el.un('mouseenter' ,this.onEventEnter, this);
10325                 el.un('mouseleave' ,this.onEventLeave, this);
10326                 el.remove();
10327             },this);
10328         },this);
10329         
10330     },
10331     
10332     renderEvents: function()
10333     {   
10334         // first make sure there is enough space..
10335         
10336         this.cells.each(function(c) {
10337         
10338             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10339         });
10340         
10341         for (var e = 0; e < this.calevents.length; e++) {
10342             var ev = this.calevents[e];
10343             var cells = ev.cells;
10344             var rows = ev.rows;
10345             
10346             for(var i =0; i < rows.length; i++) {
10347                 
10348                  
10349                 // how many rows should it span..
10350                 
10351                 var  cfg = {
10352                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10353                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10354                     
10355                     unselectable : "on",
10356                     cn : [
10357                         {
10358                             cls: 'fc-event-inner',
10359                             cn : [
10360 //                                {
10361 //                                  tag:'span',
10362 //                                  cls: 'fc-event-time',
10363 //                                  html : cells.length > 1 ? '' : ev.time
10364 //                                },
10365                                 {
10366                                   tag:'span',
10367                                   cls: 'fc-event-title',
10368                                   html : String.format('{0}', ev.title)
10369                                 }
10370                                 
10371                                 
10372                             ]
10373                         },
10374                         {
10375                             cls: 'ui-resizable-handle ui-resizable-e',
10376                             html : '&nbsp;&nbsp;&nbsp'
10377                         }
10378                         
10379                     ]
10380                 };
10381                 if (i == 0) {
10382                     cfg.cls += ' fc-event-start';
10383                 }
10384                 if ((i+1) == rows.length) {
10385                     cfg.cls += ' fc-event-end';
10386                 }
10387                 
10388                 var ctr = this.el.select('.fc-event-container',true).first();
10389                 var cg = ctr.createChild(cfg);
10390                 
10391                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10392                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10393                 cg.on('click', this.onEventClick, this, ev);
10394                 
10395                 ev.els.push(cg);
10396                 
10397                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10398                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10399                 //Roo.log(cg);
10400                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10401                 cg.setWidth(ebox.right - sbox.x -2);
10402             }
10403             
10404             
10405         }
10406         
10407     },
10408     
10409     onEventEnter: function (e, el,event,d) {
10410         this.fireEvent('evententer', this, el, event);
10411     },
10412     
10413     onEventLeave: function (e, el,event,d) {
10414         this.fireEvent('eventleave', this, el, event);
10415     },
10416     
10417     onEventClick: function (e, el,event,d) {
10418         this.fireEvent('eventclick', this, el, event);
10419     },
10420     
10421     onMonthChange: function () {
10422         this.store.load();
10423     },
10424     
10425     onLoad: function () 
10426     {   
10427         this.calevents = [];
10428         var cal = this;
10429         
10430         if(this.store.getCount() > 0){
10431             this.store.data.each(function(d){
10432                cal.addItem({
10433                     id : d.data.id,
10434                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10435                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10436                     time : d.data.start_time,
10437                     title : d.data.title,
10438                     description : d.data.description,
10439                     venue : d.data.venue
10440                 });
10441             });
10442         }
10443         
10444         this.renderEvents();
10445         
10446         if(this.loadMask){
10447             this.maskEl.hide();
10448         }
10449     },
10450     
10451     onBeforeLoad: function()
10452     {
10453         this.clearEvents();
10454         
10455         if(this.loadMask){
10456             this.maskEl.show();
10457         }
10458     }
10459 });
10460
10461  
10462  /*
10463  * - LGPL
10464  *
10465  * element
10466  * 
10467  */
10468
10469 /**
10470  * @class Roo.bootstrap.Popover
10471  * @extends Roo.bootstrap.Component
10472  * Bootstrap Popover class
10473  * @cfg {String} html contents of the popover   (or false to use children..)
10474  * @cfg {String} title of popover (or false to hide)
10475  * @cfg {String} placement how it is placed
10476  * @cfg {String} trigger click || hover (or false to trigger manually)
10477  * @cfg {String} over what (parent or false to trigger manually.)
10478  * 
10479  * @constructor
10480  * Create a new Popover
10481  * @param {Object} config The config object
10482  */
10483
10484 Roo.bootstrap.Popover = function(config){
10485     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10486 };
10487
10488 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10489     
10490     title: 'Fill in a title',
10491     html: false,
10492     
10493     placement : 'right',
10494     trigger : 'hover', // hover
10495     
10496     over: 'parent',
10497     
10498     can_build_overlaid : false,
10499     
10500     getChildContainer : function()
10501     {
10502         return this.el.select('.popover-content',true).first();
10503     },
10504     
10505     getAutoCreate : function(){
10506          Roo.log('make popover?');
10507         var cfg = {
10508            cls : 'popover roo-dynamic',
10509            style: 'display:block',
10510            cn : [
10511                 {
10512                     cls : 'arrow'
10513                 },
10514                 {
10515                     cls : 'popover-inner',
10516                     cn : [
10517                         {
10518                             tag: 'h3',
10519                             cls: 'popover-title',
10520                             html : this.title
10521                         },
10522                         {
10523                             cls : 'popover-content',
10524                             html : this.html
10525                         }
10526                     ]
10527                     
10528                 }
10529            ]
10530         };
10531         
10532         return cfg;
10533     },
10534     setTitle: function(str)
10535     {
10536         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10537     },
10538     setContent: function(str)
10539     {
10540         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10541     },
10542     // as it get's added to the bottom of the page.
10543     onRender : function(ct, position)
10544     {
10545         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10546         if(!this.el){
10547             var cfg = Roo.apply({},  this.getAutoCreate());
10548             cfg.id = Roo.id();
10549             
10550             if (this.cls) {
10551                 cfg.cls += ' ' + this.cls;
10552             }
10553             if (this.style) {
10554                 cfg.style = this.style;
10555             }
10556             Roo.log("adding to ")
10557             this.el = Roo.get(document.body).createChild(cfg, position);
10558             Roo.log(this.el);
10559         }
10560         this.initEvents();
10561     },
10562     
10563     initEvents : function()
10564     {
10565         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10566         this.el.enableDisplayMode('block');
10567         this.el.hide();
10568         if (this.over === false) {
10569             return; 
10570         }
10571         if (this.triggers === false) {
10572             return;
10573         }
10574         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10575         var triggers = this.trigger ? this.trigger.split(' ') : [];
10576         Roo.each(triggers, function(trigger) {
10577         
10578             if (trigger == 'click') {
10579                 on_el.on('click', this.toggle, this);
10580             } else if (trigger != 'manual') {
10581                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10582                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10583       
10584                 on_el.on(eventIn  ,this.enter, this);
10585                 on_el.on(eventOut, this.leave, this);
10586             }
10587         }, this);
10588         
10589     },
10590     
10591     
10592     // private
10593     timeout : null,
10594     hoverState : null,
10595     
10596     toggle : function () {
10597         this.hoverState == 'in' ? this.leave() : this.enter();
10598     },
10599     
10600     enter : function () {
10601        
10602     
10603         clearTimeout(this.timeout);
10604     
10605         this.hoverState = 'in'
10606     
10607         if (!this.delay || !this.delay.show) {
10608             this.show();
10609             return 
10610         }
10611         var _t = this;
10612         this.timeout = setTimeout(function () {
10613             if (_t.hoverState == 'in') {
10614                 _t.show();
10615             }
10616         }, this.delay.show)
10617     },
10618     leave : function() {
10619         clearTimeout(this.timeout);
10620     
10621         this.hoverState = 'out'
10622     
10623         if (!this.delay || !this.delay.hide) {
10624             this.hide();
10625             return 
10626         }
10627         var _t = this;
10628         this.timeout = setTimeout(function () {
10629             if (_t.hoverState == 'out') {
10630                 _t.hide();
10631             }
10632         }, this.delay.hide)
10633     },
10634     
10635     show : function (on_el)
10636     {
10637         if (!on_el) {
10638             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10639         }
10640         // set content.
10641         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10642         if (this.html !== false) {
10643             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10644         }
10645         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10646         if (!this.title.length) {
10647             this.el.select('.popover-title',true).hide();
10648         }
10649         
10650         var placement = typeof this.placement == 'function' ?
10651             this.placement.call(this, this.el, on_el) :
10652             this.placement;
10653             
10654         var autoToken = /\s?auto?\s?/i;
10655         var autoPlace = autoToken.test(placement);
10656         if (autoPlace) {
10657             placement = placement.replace(autoToken, '') || 'top';
10658         }
10659         
10660         //this.el.detach()
10661         //this.el.setXY([0,0]);
10662         this.el.show();
10663         this.el.dom.style.display='block';
10664         this.el.addClass(placement);
10665         
10666         //this.el.appendTo(on_el);
10667         
10668         var p = this.getPosition();
10669         var box = this.el.getBox();
10670         
10671         if (autoPlace) {
10672             // fixme..
10673         }
10674         var align = Roo.bootstrap.Popover.alignment[placement]
10675         this.el.alignTo(on_el, align[0],align[1]);
10676         //var arrow = this.el.select('.arrow',true).first();
10677         //arrow.set(align[2], 
10678         
10679         this.el.addClass('in');
10680         this.hoverState = null;
10681         
10682         if (this.el.hasClass('fade')) {
10683             // fade it?
10684         }
10685         
10686     },
10687     hide : function()
10688     {
10689         this.el.setXY([0,0]);
10690         this.el.removeClass('in');
10691         this.el.hide();
10692         
10693     }
10694     
10695 });
10696
10697 Roo.bootstrap.Popover.alignment = {
10698     'left' : ['r-l', [-10,0], 'right'],
10699     'right' : ['l-r', [10,0], 'left'],
10700     'bottom' : ['t-b', [0,10], 'top'],
10701     'top' : [ 'b-t', [0,-10], 'bottom']
10702 };
10703
10704  /*
10705  * - LGPL
10706  *
10707  * Progress
10708  * 
10709  */
10710
10711 /**
10712  * @class Roo.bootstrap.Progress
10713  * @extends Roo.bootstrap.Component
10714  * Bootstrap Progress class
10715  * @cfg {Boolean} striped striped of the progress bar
10716  * @cfg {Boolean} active animated of the progress bar
10717  * 
10718  * 
10719  * @constructor
10720  * Create a new Progress
10721  * @param {Object} config The config object
10722  */
10723
10724 Roo.bootstrap.Progress = function(config){
10725     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10726 };
10727
10728 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10729     
10730     striped : false,
10731     active: false,
10732     
10733     getAutoCreate : function(){
10734         var cfg = {
10735             tag: 'div',
10736             cls: 'progress'
10737         };
10738         
10739         
10740         if(this.striped){
10741             cfg.cls += ' progress-striped';
10742         }
10743       
10744         if(this.active){
10745             cfg.cls += ' active';
10746         }
10747         
10748         
10749         return cfg;
10750     }
10751    
10752 });
10753
10754  
10755
10756  /*
10757  * - LGPL
10758  *
10759  * ProgressBar
10760  * 
10761  */
10762
10763 /**
10764  * @class Roo.bootstrap.ProgressBar
10765  * @extends Roo.bootstrap.Component
10766  * Bootstrap ProgressBar class
10767  * @cfg {Number} aria_valuenow aria-value now
10768  * @cfg {Number} aria_valuemin aria-value min
10769  * @cfg {Number} aria_valuemax aria-value max
10770  * @cfg {String} label label for the progress bar
10771  * @cfg {String} panel (success | info | warning | danger )
10772  * @cfg {String} role role of the progress bar
10773  * @cfg {String} sr_only text
10774  * 
10775  * 
10776  * @constructor
10777  * Create a new ProgressBar
10778  * @param {Object} config The config object
10779  */
10780
10781 Roo.bootstrap.ProgressBar = function(config){
10782     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10783 };
10784
10785 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10786     
10787     aria_valuenow : 0,
10788     aria_valuemin : 0,
10789     aria_valuemax : 100,
10790     label : false,
10791     panel : false,
10792     role : false,
10793     sr_only: false,
10794     
10795     getAutoCreate : function()
10796     {
10797         
10798         var cfg = {
10799             tag: 'div',
10800             cls: 'progress-bar',
10801             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10802         };
10803         
10804         if(this.sr_only){
10805             cfg.cn = {
10806                 tag: 'span',
10807                 cls: 'sr-only',
10808                 html: this.sr_only
10809             }
10810         }
10811         
10812         if(this.role){
10813             cfg.role = this.role;
10814         }
10815         
10816         if(this.aria_valuenow){
10817             cfg['aria-valuenow'] = this.aria_valuenow;
10818         }
10819         
10820         if(this.aria_valuemin){
10821             cfg['aria-valuemin'] = this.aria_valuemin;
10822         }
10823         
10824         if(this.aria_valuemax){
10825             cfg['aria-valuemax'] = this.aria_valuemax;
10826         }
10827         
10828         if(this.label && !this.sr_only){
10829             cfg.html = this.label;
10830         }
10831         
10832         if(this.panel){
10833             cfg.cls += ' progress-bar-' + this.panel;
10834         }
10835         
10836         return cfg;
10837     },
10838     
10839     update : function(aria_valuenow)
10840     {
10841         this.aria_valuenow = aria_valuenow;
10842         
10843         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10844     }
10845    
10846 });
10847
10848  
10849
10850  /*
10851  * - LGPL
10852  *
10853  * TabPanel
10854  * 
10855  */
10856
10857 /**
10858  * @class Roo.bootstrap.TabPanel
10859  * @extends Roo.bootstrap.Component
10860  * Bootstrap TabPanel class
10861  * @cfg {Boolean} active panel active
10862  * @cfg {String} html panel content
10863  * @cfg {String} tabId tab relate id
10864  * 
10865  * 
10866  * @constructor
10867  * Create a new TabPanel
10868  * @param {Object} config The config object
10869  */
10870
10871 Roo.bootstrap.TabPanel = function(config){
10872     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10873 };
10874
10875 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10876     
10877     active: false,
10878     html: false,
10879     tabId: false,
10880     
10881     getAutoCreate : function(){
10882         var cfg = {
10883             tag: 'div',
10884             cls: 'tab-pane',
10885             html: this.html || ''
10886         };
10887         
10888         if(this.active){
10889             cfg.cls += ' active';
10890         }
10891         
10892         if(this.tabId){
10893             cfg.tabId = this.tabId;
10894         }
10895         
10896         return cfg;
10897     }
10898    
10899 });
10900
10901  
10902
10903  /*
10904  * - LGPL
10905  *
10906  * DateField
10907  * 
10908  */
10909
10910 /**
10911  * @class Roo.bootstrap.DateField
10912  * @extends Roo.bootstrap.Input
10913  * Bootstrap DateField class
10914  * @cfg {Number} weekStart default 0
10915  * @cfg {Number} weekStart default 0
10916  * @cfg {Number} viewMode default empty, (months|years)
10917  * @cfg {Number} minViewMode default empty, (months|years)
10918  * @cfg {Number} startDate default -Infinity
10919  * @cfg {Number} endDate default Infinity
10920  * @cfg {Boolean} todayHighlight default false
10921  * @cfg {Boolean} todayBtn default false
10922  * @cfg {Boolean} calendarWeeks default false
10923  * @cfg {Object} daysOfWeekDisabled default empty
10924  * 
10925  * @cfg {Boolean} keyboardNavigation default true
10926  * @cfg {String} language default en
10927  * 
10928  * @constructor
10929  * Create a new DateField
10930  * @param {Object} config The config object
10931  */
10932
10933 Roo.bootstrap.DateField = function(config){
10934     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10935      this.addEvents({
10936             /**
10937              * @event show
10938              * Fires when this field show.
10939              * @param {Roo.bootstrap.DateField} this
10940              * @param {Mixed} date The date value
10941              */
10942             show : true,
10943             /**
10944              * @event show
10945              * Fires when this field hide.
10946              * @param {Roo.bootstrap.DateField} this
10947              * @param {Mixed} date The date value
10948              */
10949             hide : true,
10950             /**
10951              * @event select
10952              * Fires when select a date.
10953              * @param {Roo.bootstrap.DateField} this
10954              * @param {Mixed} date The date value
10955              */
10956             select : true
10957         });
10958 };
10959
10960 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10961     
10962     /**
10963      * @cfg {String} format
10964      * The default date format string which can be overriden for localization support.  The format must be
10965      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10966      */
10967     format : "m/d/y",
10968     /**
10969      * @cfg {String} altFormats
10970      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10971      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10972      */
10973     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10974     
10975     weekStart : 0,
10976     
10977     viewMode : '',
10978     
10979     minViewMode : '',
10980     
10981     todayHighlight : false,
10982     
10983     todayBtn: false,
10984     
10985     language: 'en',
10986     
10987     keyboardNavigation: true,
10988     
10989     calendarWeeks: false,
10990     
10991     startDate: -Infinity,
10992     
10993     endDate: Infinity,
10994     
10995     daysOfWeekDisabled: [],
10996     
10997     _events: [],
10998     
10999     UTCDate: function()
11000     {
11001         return new Date(Date.UTC.apply(Date, arguments));
11002     },
11003     
11004     UTCToday: function()
11005     {
11006         var today = new Date();
11007         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11008     },
11009     
11010     getDate: function() {
11011             var d = this.getUTCDate();
11012             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11013     },
11014     
11015     getUTCDate: function() {
11016             return this.date;
11017     },
11018     
11019     setDate: function(d) {
11020             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11021     },
11022     
11023     setUTCDate: function(d) {
11024             this.date = d;
11025             this.setValue(this.formatDate(this.date));
11026     },
11027         
11028     onRender: function(ct, position)
11029     {
11030         
11031         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11032         
11033         this.language = this.language || 'en';
11034         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11035         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11036         
11037         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11038         this.format = this.format || 'm/d/y';
11039         this.isInline = false;
11040         this.isInput = true;
11041         this.component = this.el.select('.add-on', true).first() || false;
11042         this.component = (this.component && this.component.length === 0) ? false : this.component;
11043         this.hasInput = this.component && this.inputEL().length;
11044         
11045         if (typeof(this.minViewMode === 'string')) {
11046             switch (this.minViewMode) {
11047                 case 'months':
11048                     this.minViewMode = 1;
11049                     break;
11050                 case 'years':
11051                     this.minViewMode = 2;
11052                     break;
11053                 default:
11054                     this.minViewMode = 0;
11055                     break;
11056             }
11057         }
11058         
11059         if (typeof(this.viewMode === 'string')) {
11060             switch (this.viewMode) {
11061                 case 'months':
11062                     this.viewMode = 1;
11063                     break;
11064                 case 'years':
11065                     this.viewMode = 2;
11066                     break;
11067                 default:
11068                     this.viewMode = 0;
11069                     break;
11070             }
11071         }
11072                 
11073         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11074         
11075         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11076         
11077         this.picker().on('mousedown', this.onMousedown, this);
11078         this.picker().on('click', this.onClick, this);
11079         
11080         this.picker().addClass('datepicker-dropdown');
11081         
11082         this.startViewMode = this.viewMode;
11083         
11084         
11085         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11086             if(!this.calendarWeeks){
11087                 v.remove();
11088                 return;
11089             };
11090             
11091             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11092             v.attr('colspan', function(i, val){
11093                 return parseInt(val) + 1;
11094             });
11095         })
11096                         
11097         
11098         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11099         
11100         this.setStartDate(this.startDate);
11101         this.setEndDate(this.endDate);
11102         
11103         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11104         
11105         this.fillDow();
11106         this.fillMonths();
11107         this.update();
11108         this.showMode();
11109         
11110         if(this.isInline) {
11111             this.show();
11112         }
11113     },
11114     
11115     picker : function()
11116     {
11117         return this.el.select('.datepicker', true).first();
11118     },
11119     
11120     fillDow: function()
11121     {
11122         var dowCnt = this.weekStart;
11123         
11124         var dow = {
11125             tag: 'tr',
11126             cn: [
11127                 
11128             ]
11129         };
11130         
11131         if(this.calendarWeeks){
11132             dow.cn.push({
11133                 tag: 'th',
11134                 cls: 'cw',
11135                 html: '&nbsp;'
11136             })
11137         }
11138         
11139         while (dowCnt < this.weekStart + 7) {
11140             dow.cn.push({
11141                 tag: 'th',
11142                 cls: 'dow',
11143                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11144             });
11145         }
11146         
11147         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11148     },
11149     
11150     fillMonths: function()
11151     {    
11152         var i = 0
11153         var months = this.picker().select('>.datepicker-months td', true).first();
11154         
11155         months.dom.innerHTML = '';
11156         
11157         while (i < 12) {
11158             var month = {
11159                 tag: 'span',
11160                 cls: 'month',
11161                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11162             }
11163             
11164             months.createChild(month);
11165         }
11166         
11167     },
11168     
11169     update: function(){
11170         
11171         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11172         
11173         if (this.date < this.startDate) {
11174             this.viewDate = new Date(this.startDate);
11175         } else if (this.date > this.endDate) {
11176             this.viewDate = new Date(this.endDate);
11177         } else {
11178             this.viewDate = new Date(this.date);
11179         }
11180         
11181         this.fill();
11182     },
11183     
11184     fill: function() {
11185         var d = new Date(this.viewDate),
11186                 year = d.getUTCFullYear(),
11187                 month = d.getUTCMonth(),
11188                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11189                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11190                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11191                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11192                 currentDate = this.date && this.date.valueOf(),
11193                 today = this.UTCToday();
11194         
11195         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11196         
11197 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11198         
11199 //        this.picker.select('>tfoot th.today').
11200 //                                              .text(dates[this.language].today)
11201 //                                              .toggle(this.todayBtn !== false);
11202     
11203         this.updateNavArrows();
11204         this.fillMonths();
11205                                                 
11206         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11207         
11208         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11209          
11210         prevMonth.setUTCDate(day);
11211         
11212         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11213         
11214         var nextMonth = new Date(prevMonth);
11215         
11216         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11217         
11218         nextMonth = nextMonth.valueOf();
11219         
11220         var fillMonths = false;
11221         
11222         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11223         
11224         while(prevMonth.valueOf() < nextMonth) {
11225             var clsName = '';
11226             
11227             if (prevMonth.getUTCDay() === this.weekStart) {
11228                 if(fillMonths){
11229                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11230                 }
11231                     
11232                 fillMonths = {
11233                     tag: 'tr',
11234                     cn: []
11235                 };
11236                 
11237                 if(this.calendarWeeks){
11238                     // ISO 8601: First week contains first thursday.
11239                     // ISO also states week starts on Monday, but we can be more abstract here.
11240                     var
11241                     // Start of current week: based on weekstart/current date
11242                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11243                     // Thursday of this week
11244                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11245                     // First Thursday of year, year from thursday
11246                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11247                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11248                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11249                     
11250                     fillMonths.cn.push({
11251                         tag: 'td',
11252                         cls: 'cw',
11253                         html: calWeek
11254                     });
11255                 }
11256             }
11257             
11258             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11259                 clsName += ' old';
11260             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11261                 clsName += ' new';
11262             }
11263             if (this.todayHighlight &&
11264                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11265                 prevMonth.getUTCMonth() == today.getMonth() &&
11266                 prevMonth.getUTCDate() == today.getDate()) {
11267                 clsName += ' today';
11268             }
11269             
11270             if (currentDate && prevMonth.valueOf() === currentDate) {
11271                 clsName += ' active';
11272             }
11273             
11274             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11275                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11276                     clsName += ' disabled';
11277             }
11278             
11279             fillMonths.cn.push({
11280                 tag: 'td',
11281                 cls: 'day ' + clsName,
11282                 html: prevMonth.getDate()
11283             })
11284             
11285             prevMonth.setDate(prevMonth.getDate()+1);
11286         }
11287           
11288         var currentYear = this.date && this.date.getUTCFullYear();
11289         var currentMonth = this.date && this.date.getUTCMonth();
11290         
11291         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11292         
11293         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11294             v.removeClass('active');
11295             
11296             if(currentYear === year && k === currentMonth){
11297                 v.addClass('active');
11298             }
11299             
11300             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11301                 v.addClass('disabled');
11302             }
11303             
11304         });
11305         
11306         
11307         year = parseInt(year/10, 10) * 10;
11308         
11309         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11310         
11311         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11312         
11313         year -= 1;
11314         for (var i = -1; i < 11; i++) {
11315             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11316                 tag: 'span',
11317                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11318                 html: year
11319             })
11320             
11321             year += 1;
11322         }
11323     },
11324     
11325     showMode: function(dir) {
11326         if (dir) {
11327             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11328         }
11329         Roo.each(this.picker().select('>div',true).elements, function(v){
11330             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11331             v.hide();
11332         });
11333         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11334     },
11335     
11336     place: function()
11337     {
11338         if(this.isInline) return;
11339         
11340         this.picker().removeClass(['bottom', 'top']);
11341         
11342         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11343             /*
11344              * place to the top of element!
11345              *
11346              */
11347             
11348             this.picker().addClass('top');
11349             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11350             
11351             return;
11352         }
11353         
11354         this.picker().addClass('bottom');
11355         
11356         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11357     },
11358     
11359     parseDate : function(value){
11360         if(!value || value instanceof Date){
11361             return value;
11362         }
11363         var v = Date.parseDate(value, this.format);
11364         if (!v && this.useIso) {
11365             v = Date.parseDate(value, 'Y-m-d');
11366         }
11367         if(!v && this.altFormats){
11368             if(!this.altFormatsArray){
11369                 this.altFormatsArray = this.altFormats.split("|");
11370             }
11371             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11372                 v = Date.parseDate(value, this.altFormatsArray[i]);
11373             }
11374         }
11375         return v;
11376     },
11377     
11378     formatDate : function(date, fmt){
11379         return (!date || !(date instanceof Date)) ?
11380         date : date.dateFormat(fmt || this.format);
11381     },
11382     
11383     onFocus : function()
11384     {
11385         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11386         this.show();
11387     },
11388     
11389     onBlur : function()
11390     {
11391         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11392         this.hide();
11393     },
11394     
11395     show : function()
11396     {
11397         this.picker().show();
11398         this.update();
11399         this.place();
11400         
11401         this.fireEvent('show', this, this.date);
11402     },
11403     
11404     hide : function()
11405     {
11406         if(this.isInline) return;
11407         this.picker().hide();
11408         this.viewMode = this.startViewMode;
11409         this.showMode();
11410         
11411         this.fireEvent('hide', this, this.date);
11412         
11413     },
11414     
11415     onMousedown: function(e){
11416         e.stopPropagation();
11417         e.preventDefault();
11418     },
11419     
11420     keyup: function(e){
11421         Roo.bootstrap.DateField.superclass.keyup.call(this);
11422         this.update();
11423         
11424     },
11425
11426     setValue: function(v){
11427         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11428         
11429         this.fireEvent('select', this, this.date);
11430         
11431     },
11432     
11433     fireKey: function(e){
11434         if (!this.picker().isVisible()){
11435             if (e.keyCode == 27) // allow escape to hide and re-show picker
11436                 this.show();
11437             return;
11438         }
11439         var dateChanged = false,
11440         dir, day, month,
11441         newDate, newViewDate;
11442         switch(e.keyCode){
11443             case 27: // escape
11444                 this.hide();
11445                 e.preventDefault();
11446                 break;
11447             case 37: // left
11448             case 39: // right
11449                 if (!this.keyboardNavigation) break;
11450                 dir = e.keyCode == 37 ? -1 : 1;
11451                 
11452                 if (e.ctrlKey){
11453                     newDate = this.moveYear(this.date, dir);
11454                     newViewDate = this.moveYear(this.viewDate, dir);
11455                 } else if (e.shiftKey){
11456                     newDate = this.moveMonth(this.date, dir);
11457                     newViewDate = this.moveMonth(this.viewDate, dir);
11458                 } else {
11459                     newDate = new Date(this.date);
11460                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11461                     newViewDate = new Date(this.viewDate);
11462                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11463                 }
11464                 if (this.dateWithinRange(newDate)){
11465                     this.date = newDate;
11466                     this.viewDate = newViewDate;
11467                     this.setValue(this.formatDate(this.date));
11468                     this.update();
11469                     e.preventDefault();
11470                     dateChanged = true;
11471                 }
11472                 break;
11473             case 38: // up
11474             case 40: // down
11475                 if (!this.keyboardNavigation) break;
11476                 dir = e.keyCode == 38 ? -1 : 1;
11477                 if (e.ctrlKey){
11478                     newDate = this.moveYear(this.date, dir);
11479                     newViewDate = this.moveYear(this.viewDate, dir);
11480                 } else if (e.shiftKey){
11481                     newDate = this.moveMonth(this.date, dir);
11482                     newViewDate = this.moveMonth(this.viewDate, dir);
11483                 } else {
11484                     newDate = new Date(this.date);
11485                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11486                     newViewDate = new Date(this.viewDate);
11487                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11488                 }
11489                 if (this.dateWithinRange(newDate)){
11490                     this.date = newDate;
11491                     this.viewDate = newViewDate;
11492                     this.setValue(this.formatDate(this.date));
11493                     this.update();
11494                     e.preventDefault();
11495                     dateChanged = true;
11496                 }
11497                 break;
11498             case 13: // enter
11499                 this.setValue(this.formatDate(this.date));
11500                 this.hide();
11501                 e.preventDefault();
11502                 break;
11503             case 9: // tab
11504                 this.setValue(this.formatDate(this.date));
11505                 this.hide();
11506                 break;
11507         }
11508     },
11509     
11510     
11511     onClick: function(e) {
11512         e.stopPropagation();
11513         e.preventDefault();
11514         
11515         var target = e.getTarget();
11516         
11517         if(target.nodeName.toLowerCase() === 'i'){
11518             target = Roo.get(target).dom.parentNode;
11519         }
11520         
11521         var nodeName = target.nodeName;
11522         var className = target.className;
11523         var html = target.innerHTML;
11524         
11525         switch(nodeName.toLowerCase()) {
11526             case 'th':
11527                 switch(className) {
11528                     case 'switch':
11529                         this.showMode(1);
11530                         break;
11531                     case 'prev':
11532                     case 'next':
11533                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11534                         switch(this.viewMode){
11535                                 case 0:
11536                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11537                                         break;
11538                                 case 1:
11539                                 case 2:
11540                                         this.viewDate = this.moveYear(this.viewDate, dir);
11541                                         break;
11542                         }
11543                         this.fill();
11544                         break;
11545                     case 'today':
11546                         var date = new Date();
11547                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11548                         this.fill()
11549                         this.setValue(this.formatDate(this.date));
11550                         this.hide();
11551                         break;
11552                 }
11553                 break;
11554             case 'span':
11555                 if (className.indexOf('disabled') === -1) {
11556                     this.viewDate.setUTCDate(1);
11557                     if (className.indexOf('month') !== -1) {
11558                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11559                     } else {
11560                         var year = parseInt(html, 10) || 0;
11561                         this.viewDate.setUTCFullYear(year);
11562                         
11563                     }
11564                     this.showMode(-1);
11565                     this.fill();
11566                 }
11567                 break;
11568                 
11569             case 'td':
11570                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11571                     var day = parseInt(html, 10) || 1;
11572                     var year = this.viewDate.getUTCFullYear(),
11573                         month = this.viewDate.getUTCMonth();
11574
11575                     if (className.indexOf('old') !== -1) {
11576                         if(month === 0 ){
11577                             month = 11;
11578                             year -= 1;
11579                         }else{
11580                             month -= 1;
11581                         }
11582                     } else if (className.indexOf('new') !== -1) {
11583                         if (month == 11) {
11584                             month = 0;
11585                             year += 1;
11586                         } else {
11587                             month += 1;
11588                         }
11589                     }
11590                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11591                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11592                     this.fill();
11593                     this.setValue(this.formatDate(this.date));
11594                     this.hide();
11595                 }
11596                 break;
11597         }
11598     },
11599     
11600     setStartDate: function(startDate){
11601         this.startDate = startDate || -Infinity;
11602         if (this.startDate !== -Infinity) {
11603             this.startDate = this.parseDate(this.startDate);
11604         }
11605         this.update();
11606         this.updateNavArrows();
11607     },
11608
11609     setEndDate: function(endDate){
11610         this.endDate = endDate || Infinity;
11611         if (this.endDate !== Infinity) {
11612             this.endDate = this.parseDate(this.endDate);
11613         }
11614         this.update();
11615         this.updateNavArrows();
11616     },
11617     
11618     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11619         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11620         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11621             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11622         }
11623         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11624             return parseInt(d, 10);
11625         });
11626         this.update();
11627         this.updateNavArrows();
11628     },
11629     
11630     updateNavArrows: function() {
11631         var d = new Date(this.viewDate),
11632         year = d.getUTCFullYear(),
11633         month = d.getUTCMonth();
11634         
11635         Roo.each(this.picker().select('.prev', true).elements, function(v){
11636             v.show();
11637             switch (this.viewMode) {
11638                 case 0:
11639
11640                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11641                         v.hide();
11642                     }
11643                     break;
11644                 case 1:
11645                 case 2:
11646                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11647                         v.hide();
11648                     }
11649                     break;
11650             }
11651         });
11652         
11653         Roo.each(this.picker().select('.next', true).elements, function(v){
11654             v.show();
11655             switch (this.viewMode) {
11656                 case 0:
11657
11658                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11659                         v.hide();
11660                     }
11661                     break;
11662                 case 1:
11663                 case 2:
11664                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11665                         v.hide();
11666                     }
11667                     break;
11668             }
11669         })
11670     },
11671     
11672     moveMonth: function(date, dir){
11673         if (!dir) return date;
11674         var new_date = new Date(date.valueOf()),
11675         day = new_date.getUTCDate(),
11676         month = new_date.getUTCMonth(),
11677         mag = Math.abs(dir),
11678         new_month, test;
11679         dir = dir > 0 ? 1 : -1;
11680         if (mag == 1){
11681             test = dir == -1
11682             // If going back one month, make sure month is not current month
11683             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11684             ? function(){
11685                 return new_date.getUTCMonth() == month;
11686             }
11687             // If going forward one month, make sure month is as expected
11688             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11689             : function(){
11690                 return new_date.getUTCMonth() != new_month;
11691             };
11692             new_month = month + dir;
11693             new_date.setUTCMonth(new_month);
11694             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11695             if (new_month < 0 || new_month > 11)
11696                 new_month = (new_month + 12) % 12;
11697         } else {
11698             // For magnitudes >1, move one month at a time...
11699             for (var i=0; i<mag; i++)
11700                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11701                 new_date = this.moveMonth(new_date, dir);
11702             // ...then reset the day, keeping it in the new month
11703             new_month = new_date.getUTCMonth();
11704             new_date.setUTCDate(day);
11705             test = function(){
11706                 return new_month != new_date.getUTCMonth();
11707             };
11708         }
11709         // Common date-resetting loop -- if date is beyond end of month, make it
11710         // end of month
11711         while (test()){
11712             new_date.setUTCDate(--day);
11713             new_date.setUTCMonth(new_month);
11714         }
11715         return new_date;
11716     },
11717
11718     moveYear: function(date, dir){
11719         return this.moveMonth(date, dir*12);
11720     },
11721
11722     dateWithinRange: function(date){
11723         return date >= this.startDate && date <= this.endDate;
11724     },
11725
11726     
11727     remove: function() {
11728         this.picker().remove();
11729     }
11730    
11731 });
11732
11733 Roo.apply(Roo.bootstrap.DateField,  {
11734     
11735     head : {
11736         tag: 'thead',
11737         cn: [
11738         {
11739             tag: 'tr',
11740             cn: [
11741             {
11742                 tag: 'th',
11743                 cls: 'prev',
11744                 html: '<i class="icon-arrow-left"/>'
11745             },
11746             {
11747                 tag: 'th',
11748                 cls: 'switch',
11749                 colspan: '5'
11750             },
11751             {
11752                 tag: 'th',
11753                 cls: 'next',
11754                 html: '<i class="icon-arrow-right"/>'
11755             }
11756
11757             ]
11758         }
11759         ]
11760     },
11761     
11762     content : {
11763         tag: 'tbody',
11764         cn: [
11765         {
11766             tag: 'tr',
11767             cn: [
11768             {
11769                 tag: 'td',
11770                 colspan: '7'
11771             }
11772             ]
11773         }
11774         ]
11775     },
11776     
11777     footer : {
11778         tag: 'tfoot',
11779         cn: [
11780         {
11781             tag: 'tr',
11782             cn: [
11783             {
11784                 tag: 'th',
11785                 colspan: '7',
11786                 cls: 'today'
11787             }
11788                     
11789             ]
11790         }
11791         ]
11792     },
11793     
11794     dates:{
11795         en: {
11796             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11797             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11798             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11799             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11800             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11801             today: "Today"
11802         }
11803     },
11804     
11805     modes: [
11806     {
11807         clsName: 'days',
11808         navFnc: 'Month',
11809         navStep: 1
11810     },
11811     {
11812         clsName: 'months',
11813         navFnc: 'FullYear',
11814         navStep: 1
11815     },
11816     {
11817         clsName: 'years',
11818         navFnc: 'FullYear',
11819         navStep: 10
11820     }]
11821 });
11822
11823 Roo.apply(Roo.bootstrap.DateField,  {
11824   
11825     template : {
11826         tag: 'div',
11827         cls: 'datepicker dropdown-menu',
11828         cn: [
11829         {
11830             tag: 'div',
11831             cls: 'datepicker-days',
11832             cn: [
11833             {
11834                 tag: 'table',
11835                 cls: 'table-condensed',
11836                 cn:[
11837                 Roo.bootstrap.DateField.head,
11838                 {
11839                     tag: 'tbody'
11840                 },
11841                 Roo.bootstrap.DateField.footer
11842                 ]
11843             }
11844             ]
11845         },
11846         {
11847             tag: 'div',
11848             cls: 'datepicker-months',
11849             cn: [
11850             {
11851                 tag: 'table',
11852                 cls: 'table-condensed',
11853                 cn:[
11854                 Roo.bootstrap.DateField.head,
11855                 Roo.bootstrap.DateField.content,
11856                 Roo.bootstrap.DateField.footer
11857                 ]
11858             }
11859             ]
11860         },
11861         {
11862             tag: 'div',
11863             cls: 'datepicker-years',
11864             cn: [
11865             {
11866                 tag: 'table',
11867                 cls: 'table-condensed',
11868                 cn:[
11869                 Roo.bootstrap.DateField.head,
11870                 Roo.bootstrap.DateField.content,
11871                 Roo.bootstrap.DateField.footer
11872                 ]
11873             }
11874             ]
11875         }
11876         ]
11877     }
11878 });
11879
11880  
11881
11882  /*
11883  * - LGPL
11884  *
11885  * TimeField
11886  * 
11887  */
11888
11889 /**
11890  * @class Roo.bootstrap.TimeField
11891  * @extends Roo.bootstrap.Input
11892  * Bootstrap DateField class
11893  * 
11894  * 
11895  * @constructor
11896  * Create a new TimeField
11897  * @param {Object} config The config object
11898  */
11899
11900 Roo.bootstrap.TimeField = function(config){
11901     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11902     this.addEvents({
11903             /**
11904              * @event show
11905              * Fires when this field show.
11906              * @param {Roo.bootstrap.DateField} this
11907              * @param {Mixed} date The date value
11908              */
11909             show : true,
11910             /**
11911              * @event show
11912              * Fires when this field hide.
11913              * @param {Roo.bootstrap.DateField} this
11914              * @param {Mixed} date The date value
11915              */
11916             hide : true,
11917             /**
11918              * @event select
11919              * Fires when select a date.
11920              * @param {Roo.bootstrap.DateField} this
11921              * @param {Mixed} date The date value
11922              */
11923             select : true
11924         });
11925 };
11926
11927 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11928     
11929     /**
11930      * @cfg {String} format
11931      * The default time format string which can be overriden for localization support.  The format must be
11932      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11933      */
11934     format : "H:i",
11935        
11936     onRender: function(ct, position)
11937     {
11938         
11939         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11940                 
11941         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11942         
11943         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11944         
11945         this.pop = this.picker().select('>.datepicker-time',true).first();
11946         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11947         
11948         this.picker().on('mousedown', this.onMousedown, this);
11949         this.picker().on('click', this.onClick, this);
11950         
11951         this.picker().addClass('datepicker-dropdown');
11952     
11953         this.fillTime();
11954         this.update();
11955             
11956         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11957         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11958         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11959         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11960         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11961         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11962
11963     },
11964     
11965     fireKey: function(e){
11966         if (!this.picker().isVisible()){
11967             if (e.keyCode == 27) // allow escape to hide and re-show picker
11968                 this.show();
11969             return;
11970         }
11971
11972         e.preventDefault();
11973         
11974         switch(e.keyCode){
11975             case 27: // escape
11976                 this.hide();
11977                 break;
11978             case 37: // left
11979             case 39: // right
11980                 this.onTogglePeriod();
11981                 break;
11982             case 38: // up
11983                 this.onIncrementMinutes();
11984                 break;
11985             case 40: // down
11986                 this.onDecrementMinutes();
11987                 break;
11988             case 13: // enter
11989             case 9: // tab
11990                 this.setTime();
11991                 break;
11992         }
11993     },
11994     
11995     onClick: function(e) {
11996         e.stopPropagation();
11997         e.preventDefault();
11998     },
11999     
12000     picker : function()
12001     {
12002         return this.el.select('.datepicker', true).first();
12003     },
12004     
12005     fillTime: function()
12006     {    
12007         var time = this.pop.select('tbody', true).first();
12008         
12009         time.dom.innerHTML = '';
12010         
12011         time.createChild({
12012             tag: 'tr',
12013             cn: [
12014                 {
12015                     tag: 'td',
12016                     cn: [
12017                         {
12018                             tag: 'a',
12019                             href: '#',
12020                             cls: 'btn',
12021                             cn: [
12022                                 {
12023                                     tag: 'span',
12024                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12025                                 }
12026                             ]
12027                         } 
12028                     ]
12029                 },
12030                 {
12031                     tag: 'td',
12032                     cls: 'separator'
12033                 },
12034                 {
12035                     tag: 'td',
12036                     cn: [
12037                         {
12038                             tag: 'a',
12039                             href: '#',
12040                             cls: 'btn',
12041                             cn: [
12042                                 {
12043                                     tag: 'span',
12044                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12045                                 }
12046                             ]
12047                         }
12048                     ]
12049                 },
12050                 {
12051                     tag: 'td',
12052                     cls: 'separator'
12053                 }
12054             ]
12055         });
12056         
12057         time.createChild({
12058             tag: 'tr',
12059             cn: [
12060                 {
12061                     tag: 'td',
12062                     cn: [
12063                         {
12064                             tag: 'span',
12065                             cls: 'timepicker-hour',
12066                             html: '00'
12067                         }  
12068                     ]
12069                 },
12070                 {
12071                     tag: 'td',
12072                     cls: 'separator',
12073                     html: ':'
12074                 },
12075                 {
12076                     tag: 'td',
12077                     cn: [
12078                         {
12079                             tag: 'span',
12080                             cls: 'timepicker-minute',
12081                             html: '00'
12082                         }  
12083                     ]
12084                 },
12085                 {
12086                     tag: 'td',
12087                     cls: 'separator'
12088                 },
12089                 {
12090                     tag: 'td',
12091                     cn: [
12092                         {
12093                             tag: 'button',
12094                             type: 'button',
12095                             cls: 'btn btn-primary period',
12096                             html: 'AM'
12097                             
12098                         }
12099                     ]
12100                 }
12101             ]
12102         });
12103         
12104         time.createChild({
12105             tag: 'tr',
12106             cn: [
12107                 {
12108                     tag: 'td',
12109                     cn: [
12110                         {
12111                             tag: 'a',
12112                             href: '#',
12113                             cls: 'btn',
12114                             cn: [
12115                                 {
12116                                     tag: 'span',
12117                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12118                                 }
12119                             ]
12120                         }
12121                     ]
12122                 },
12123                 {
12124                     tag: 'td',
12125                     cls: 'separator'
12126                 },
12127                 {
12128                     tag: 'td',
12129                     cn: [
12130                         {
12131                             tag: 'a',
12132                             href: '#',
12133                             cls: 'btn',
12134                             cn: [
12135                                 {
12136                                     tag: 'span',
12137                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12138                                 }
12139                             ]
12140                         }
12141                     ]
12142                 },
12143                 {
12144                     tag: 'td',
12145                     cls: 'separator'
12146                 }
12147             ]
12148         });
12149         
12150     },
12151     
12152     update: function()
12153     {
12154         
12155         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12156         
12157         this.fill();
12158     },
12159     
12160     fill: function() 
12161     {
12162         var hours = this.time.getHours();
12163         var minutes = this.time.getMinutes();
12164         var period = 'AM';
12165         
12166         if(hours > 11){
12167             period = 'PM';
12168         }
12169         
12170         if(hours == 0){
12171             hours = 12;
12172         }
12173         
12174         
12175         if(hours > 12){
12176             hours = hours - 12;
12177         }
12178         
12179         if(hours < 10){
12180             hours = '0' + hours;
12181         }
12182         
12183         if(minutes < 10){
12184             minutes = '0' + minutes;
12185         }
12186         
12187         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12188         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12189         this.pop.select('button', true).first().dom.innerHTML = period;
12190         
12191     },
12192     
12193     place: function()
12194     {   
12195         this.picker().removeClass(['bottom', 'top']);
12196         
12197         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12198             /*
12199              * place to the top of element!
12200              *
12201              */
12202             
12203             this.picker().addClass('top');
12204             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12205             
12206             return;
12207         }
12208         
12209         this.picker().addClass('bottom');
12210         
12211         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12212     },
12213   
12214     onFocus : function()
12215     {
12216         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12217         this.show();
12218     },
12219     
12220     onBlur : function()
12221     {
12222         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12223         this.hide();
12224     },
12225     
12226     show : function()
12227     {
12228         this.picker().show();
12229         this.pop.show();
12230         this.update();
12231         this.place();
12232         
12233         this.fireEvent('show', this, this.date);
12234     },
12235     
12236     hide : function()
12237     {
12238         this.picker().hide();
12239         this.pop.hide();
12240         
12241         this.fireEvent('hide', this, this.date);
12242     },
12243     
12244     setTime : function()
12245     {
12246         this.hide();
12247         this.setValue(this.time.format(this.format));
12248         
12249         this.fireEvent('select', this, this.date);
12250         
12251         
12252     },
12253     
12254     onMousedown: function(e){
12255         e.stopPropagation();
12256         e.preventDefault();
12257     },
12258     
12259     onIncrementHours: function()
12260     {
12261         Roo.log('onIncrementHours');
12262         this.time = this.time.add(Date.HOUR, 1);
12263         this.update();
12264         
12265     },
12266     
12267     onDecrementHours: function()
12268     {
12269         Roo.log('onDecrementHours');
12270         this.time = this.time.add(Date.HOUR, -1);
12271         this.update();
12272     },
12273     
12274     onIncrementMinutes: function()
12275     {
12276         Roo.log('onIncrementMinutes');
12277         this.time = this.time.add(Date.MINUTE, 1);
12278         this.update();
12279     },
12280     
12281     onDecrementMinutes: function()
12282     {
12283         Roo.log('onDecrementMinutes');
12284         this.time = this.time.add(Date.MINUTE, -1);
12285         this.update();
12286     },
12287     
12288     onTogglePeriod: function()
12289     {
12290         Roo.log('onTogglePeriod');
12291         this.time = this.time.add(Date.HOUR, 12);
12292         this.update();
12293     }
12294     
12295    
12296 });
12297
12298 Roo.apply(Roo.bootstrap.TimeField,  {
12299     
12300     content : {
12301         tag: 'tbody',
12302         cn: [
12303             {
12304                 tag: 'tr',
12305                 cn: [
12306                 {
12307                     tag: 'td',
12308                     colspan: '7'
12309                 }
12310                 ]
12311             }
12312         ]
12313     },
12314     
12315     footer : {
12316         tag: 'tfoot',
12317         cn: [
12318             {
12319                 tag: 'tr',
12320                 cn: [
12321                 {
12322                     tag: 'th',
12323                     colspan: '7',
12324                     cls: '',
12325                     cn: [
12326                         {
12327                             tag: 'button',
12328                             cls: 'btn btn-info ok',
12329                             html: 'OK'
12330                         }
12331                     ]
12332                 }
12333
12334                 ]
12335             }
12336         ]
12337     }
12338 });
12339
12340 Roo.apply(Roo.bootstrap.TimeField,  {
12341   
12342     template : {
12343         tag: 'div',
12344         cls: 'datepicker dropdown-menu',
12345         cn: [
12346             {
12347                 tag: 'div',
12348                 cls: 'datepicker-time',
12349                 cn: [
12350                 {
12351                     tag: 'table',
12352                     cls: 'table-condensed',
12353                     cn:[
12354                     Roo.bootstrap.TimeField.content,
12355                     Roo.bootstrap.TimeField.footer
12356                     ]
12357                 }
12358                 ]
12359             }
12360         ]
12361     }
12362 });
12363
12364  
12365
12366  /*
12367  * - LGPL
12368  *
12369  * CheckBox
12370  * 
12371  */
12372
12373 /**
12374  * @class Roo.bootstrap.CheckBox
12375  * @extends Roo.bootstrap.Input
12376  * Bootstrap CheckBox class
12377  * 
12378  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12379  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12380  * @cfg {String} boxLabel The text that appears beside the checkbox
12381  * @cfg {Boolean} checked initnal the element
12382  * 
12383  * @constructor
12384  * Create a new CheckBox
12385  * @param {Object} config The config object
12386  */
12387
12388 Roo.bootstrap.CheckBox = function(config){
12389     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12390    
12391         this.addEvents({
12392             /**
12393             * @event check
12394             * Fires when the element is checked or unchecked.
12395             * @param {Roo.bootstrap.CheckBox} this This input
12396             * @param {Boolean} checked The new checked value
12397             */
12398            check : true
12399         });
12400 };
12401
12402 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12403     
12404     inputType: 'checkbox',
12405     inputValue: 1,
12406     valueOff: 0,
12407     boxLabel: false,
12408     checked: false,
12409     
12410     getAutoCreate : function()
12411     {
12412         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12413         
12414         var id = Roo.id();
12415         
12416         var cfg = {};
12417         
12418         cfg.cls = 'form-group' //input-group
12419         
12420         var input =  {
12421             tag: 'input',
12422             id : id,
12423             type : this.inputType,
12424             value : (!this.checked) ? this.valueOff : this.inputValue,
12425             cls : 'form-box',
12426             placeholder : this.placeholder || ''
12427             
12428         };
12429         
12430         if (this.disabled) {
12431             input.disabled=true;
12432         }
12433         
12434         if(this.checked){
12435             input.checked = this.checked;
12436         }
12437         
12438         if (this.name) {
12439             input.name = this.name;
12440         }
12441         
12442         if (this.size) {
12443             input.cls += ' input-' + this.size;
12444         }
12445         
12446         var settings=this;
12447         ['xs','sm','md','lg'].map(function(size){
12448             if (settings[size]) {
12449                 cfg.cls += ' col-' + size + '-' + settings[size];
12450             }
12451         });
12452         
12453         var inputblock = input;
12454         
12455         if (this.before || this.after) {
12456             
12457             inputblock = {
12458                 cls : 'input-group',
12459                 cn :  [] 
12460             };
12461             if (this.before) {
12462                 inputblock.cn.push({
12463                     tag :'span',
12464                     cls : 'input-group-addon',
12465                     html : this.before
12466                 });
12467             }
12468             inputblock.cn.push(input);
12469             if (this.after) {
12470                 inputblock.cn.push({
12471                     tag :'span',
12472                     cls : 'input-group-addon',
12473                     html : this.after
12474                 });
12475             }
12476             
12477         };
12478         
12479         if (align ==='left' && this.fieldLabel.length) {
12480                 Roo.log("left and has label");
12481                 cfg.cn = [
12482                     
12483                     {
12484                         tag: 'label',
12485                         'for' :  id,
12486                         cls : 'control-label col-md-' + this.labelWidth,
12487                         html : this.fieldLabel
12488                         
12489                     },
12490                     {
12491                         cls : "col-md-" + (12 - this.labelWidth), 
12492                         cn: [
12493                             inputblock
12494                         ]
12495                     }
12496                     
12497                 ];
12498         } else if ( this.fieldLabel.length) {
12499                 Roo.log(" label");
12500                 cfg.cn = [
12501                    
12502                     {
12503                         tag: this.boxLabel ? 'span' : 'label',
12504                         'for': id,
12505                         cls: 'control-label box-input-label',
12506                         //cls : 'input-group-addon',
12507                         html : this.fieldLabel
12508                         
12509                     },
12510                     
12511                     inputblock
12512                     
12513                 ];
12514
12515         } else {
12516             
12517                    Roo.log(" no label && no align");
12518                 cfg.cn = [
12519                     
12520                         inputblock
12521                     
12522                 ];
12523                 
12524                 
12525         };
12526         
12527         if(this.boxLabel){
12528             cfg.cn.push({
12529                 tag: 'label',
12530                 'for': id,
12531                 cls: 'box-label',
12532                 html: this.boxLabel
12533             })
12534         }
12535         
12536         return cfg;
12537         
12538     },
12539     
12540     /**
12541      * return the real input element.
12542      */
12543     inputEl: function ()
12544     {
12545         return this.el.select('input.form-box',true).first();
12546     },
12547     
12548     initEvents : function()
12549     {
12550 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12551         
12552         this.inputEl().on('click', this.onClick,  this);
12553         
12554     },
12555     
12556     onClick : function()
12557     {   
12558         this.setChecked(!this.checked);
12559     },
12560     
12561     setChecked : function(state,suppressEvent)
12562     {
12563         this.checked = state;
12564         
12565         this.inputEl().dom.checked = state;
12566         
12567         if(suppressEvent !== true){
12568             this.fireEvent('check', this, state);
12569         }
12570         
12571         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12572         
12573     },
12574     
12575     setValue : function(v,suppressEvent)
12576     {
12577         Roo.log('run');
12578         Roo.log(this.fieldLabel);
12579         Roo.log(this.inputValue);
12580         Roo.log(v);
12581         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12582     }
12583     
12584 });
12585
12586  
12587 /*
12588  * - LGPL
12589  *
12590  * Radio
12591  * 
12592  */
12593
12594 /**
12595  * @class Roo.bootstrap.Radio
12596  * @extends Roo.bootstrap.CheckBox
12597  * Bootstrap Radio class
12598
12599  * @constructor
12600  * Create a new Radio
12601  * @param {Object} config The config object
12602  */
12603
12604 Roo.bootstrap.Radio = function(config){
12605     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12606    
12607 };
12608
12609 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12610     
12611     inputType: 'radio',
12612     inputValue: '',
12613     valueOff: '',
12614     
12615     getAutoCreate : function()
12616     {
12617         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12618         
12619         var id = Roo.id();
12620         
12621         var cfg = {};
12622         
12623         cfg.cls = 'form-group' //input-group
12624         
12625         var input =  {
12626             tag: 'input',
12627             id : id,
12628             type : this.inputType,
12629             value : (!this.checked) ? this.valueOff : this.inputValue,
12630             cls : 'form-box',
12631             placeholder : this.placeholder || ''
12632             
12633         };
12634         
12635         if (this.disabled) {
12636             input.disabled=true;
12637         }
12638         
12639         if(this.checked){
12640             input.checked = this.checked;
12641         }
12642         
12643         if (this.name) {
12644             input.name = this.name;
12645         }
12646         
12647         if (this.size) {
12648             input.cls += ' input-' + this.size;
12649         }
12650         
12651         var settings=this;
12652         ['xs','sm','md','lg'].map(function(size){
12653             if (settings[size]) {
12654                 cfg.cls += ' col-' + size + '-' + settings[size];
12655             }
12656         });
12657         
12658         var inputblock = input;
12659         
12660         if (this.before || this.after) {
12661             
12662             inputblock = {
12663                 cls : 'input-group',
12664                 cn :  [] 
12665             };
12666             if (this.before) {
12667                 inputblock.cn.push({
12668                     tag :'span',
12669                     cls : 'input-group-addon',
12670                     html : this.before
12671                 });
12672             }
12673             inputblock.cn.push(input);
12674             if (this.after) {
12675                 inputblock.cn.push({
12676                     tag :'span',
12677                     cls : 'input-group-addon',
12678                     html : this.after
12679                 });
12680             }
12681             
12682         };
12683         
12684         if (align ==='left' && this.fieldLabel.length) {
12685                 Roo.log("left and has label");
12686                 cfg.cn = [
12687                     
12688                     {
12689                         tag: 'label',
12690                         'for' :  id,
12691                         cls : 'control-label col-md-' + this.labelWidth,
12692                         html : this.fieldLabel
12693                         
12694                     },
12695                     {
12696                         cls : "col-md-" + (12 - this.labelWidth), 
12697                         cn: [
12698                             inputblock
12699                         ]
12700                     }
12701                     
12702                 ];
12703         } else if ( this.fieldLabel.length) {
12704                 Roo.log(" label");
12705                  cfg.cn = [
12706                    
12707                     {
12708                         tag: 'label',
12709                         'for': id,
12710                         cls: 'control-label box-input-label',
12711                         //cls : 'input-group-addon',
12712                         html : this.fieldLabel
12713                         
12714                     },
12715                     
12716                     inputblock
12717                     
12718                 ];
12719
12720         } else {
12721             
12722                    Roo.log(" no label && no align");
12723                 cfg.cn = [
12724                     
12725                         inputblock
12726                     
12727                 ];
12728                 
12729                 
12730         };
12731         
12732         if(this.boxLabel){
12733             cfg.cn.push({
12734                 tag: 'label',
12735                 'for': id,
12736                 cls: 'box-label',
12737                 html: this.boxLabel
12738             })
12739         }
12740         
12741         return cfg;
12742         
12743     },
12744    
12745     onClick : function()
12746     {   
12747         this.setChecked(true);
12748     },
12749     
12750     setChecked : function(state,suppressEvent)
12751     {
12752         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12753             v.dom.checked = false;
12754         });
12755         
12756         this.checked = state;
12757         this.inputEl().dom.checked = state;
12758         
12759         if(suppressEvent !== true){
12760             this.fireEvent('check', this, state);
12761         }
12762         
12763         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12764         
12765     },
12766     
12767     getGroupValue : function()
12768     {
12769         var value = ''
12770         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12771             if(v.dom.checked == true){
12772                 value = v.dom.value;
12773             }
12774         });
12775         
12776         return value;
12777     },
12778     
12779     /**
12780      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12781      * @return {Mixed} value The field value
12782      */
12783     getValue : function(){
12784         return this.getGroupValue();
12785     }
12786     
12787 });
12788
12789  
12790 //<script type="text/javascript">
12791
12792 /*
12793  * Based  Ext JS Library 1.1.1
12794  * Copyright(c) 2006-2007, Ext JS, LLC.
12795  * LGPL
12796  *
12797  */
12798  
12799 /**
12800  * @class Roo.HtmlEditorCore
12801  * @extends Roo.Component
12802  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12803  *
12804  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12805  */
12806
12807 Roo.HtmlEditorCore = function(config){
12808     
12809     
12810     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12811     this.addEvents({
12812         /**
12813          * @event initialize
12814          * Fires when the editor is fully initialized (including the iframe)
12815          * @param {Roo.HtmlEditorCore} this
12816          */
12817         initialize: true,
12818         /**
12819          * @event activate
12820          * Fires when the editor is first receives the focus. Any insertion must wait
12821          * until after this event.
12822          * @param {Roo.HtmlEditorCore} this
12823          */
12824         activate: true,
12825          /**
12826          * @event beforesync
12827          * Fires before the textarea is updated with content from the editor iframe. Return false
12828          * to cancel the sync.
12829          * @param {Roo.HtmlEditorCore} this
12830          * @param {String} html
12831          */
12832         beforesync: true,
12833          /**
12834          * @event beforepush
12835          * Fires before the iframe editor is updated with content from the textarea. Return false
12836          * to cancel the push.
12837          * @param {Roo.HtmlEditorCore} this
12838          * @param {String} html
12839          */
12840         beforepush: true,
12841          /**
12842          * @event sync
12843          * Fires when the textarea is updated with content from the editor iframe.
12844          * @param {Roo.HtmlEditorCore} this
12845          * @param {String} html
12846          */
12847         sync: true,
12848          /**
12849          * @event push
12850          * Fires when the iframe editor is updated with content from the textarea.
12851          * @param {Roo.HtmlEditorCore} this
12852          * @param {String} html
12853          */
12854         push: true,
12855         
12856         /**
12857          * @event editorevent
12858          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12859          * @param {Roo.HtmlEditorCore} this
12860          */
12861         editorevent: true
12862     });
12863      
12864 };
12865
12866
12867 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12868
12869
12870      /**
12871      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12872      */
12873     
12874     owner : false,
12875     
12876      /**
12877      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12878      *                        Roo.resizable.
12879      */
12880     resizable : false,
12881      /**
12882      * @cfg {Number} height (in pixels)
12883      */   
12884     height: 300,
12885    /**
12886      * @cfg {Number} width (in pixels)
12887      */   
12888     width: 500,
12889     
12890     /**
12891      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12892      * 
12893      */
12894     stylesheets: false,
12895     
12896     // id of frame..
12897     frameId: false,
12898     
12899     // private properties
12900     validationEvent : false,
12901     deferHeight: true,
12902     initialized : false,
12903     activated : false,
12904     sourceEditMode : false,
12905     onFocus : Roo.emptyFn,
12906     iframePad:3,
12907     hideMode:'offsets',
12908     
12909      
12910     
12911
12912     /**
12913      * Protected method that will not generally be called directly. It
12914      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12915      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12916      */
12917     getDocMarkup : function(){
12918         // body styles..
12919         var st = '';
12920         Roo.log(this.stylesheets);
12921         
12922         // inherit styels from page...?? 
12923         if (this.stylesheets === false) {
12924             
12925             Roo.get(document.head).select('style').each(function(node) {
12926                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12927             });
12928             
12929             Roo.get(document.head).select('link').each(function(node) { 
12930                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12931             });
12932             
12933         } else if (!this.stylesheets.length) {
12934                 // simple..
12935                 st = '<style type="text/css">' +
12936                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12937                    '</style>';
12938         } else {
12939             Roo.each(this.stylesheets, function(s) {
12940                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12941             });
12942             
12943         }
12944         
12945         st +=  '<style type="text/css">' +
12946             'IMG { cursor: pointer } ' +
12947         '</style>';
12948
12949         
12950         return '<html><head>' + st  +
12951             //<style type="text/css">' +
12952             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12953             //'</style>' +
12954             ' </head><body class="roo-htmleditor-body"></body></html>';
12955     },
12956
12957     // private
12958     onRender : function(ct, position)
12959     {
12960         var _t = this;
12961         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12962         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12963         
12964         
12965         this.el.dom.style.border = '0 none';
12966         this.el.dom.setAttribute('tabIndex', -1);
12967         this.el.addClass('x-hidden hide');
12968         
12969         
12970         
12971         if(Roo.isIE){ // fix IE 1px bogus margin
12972             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12973         }
12974        
12975         
12976         this.frameId = Roo.id();
12977         
12978          
12979         
12980         var iframe = this.owner.wrap.createChild({
12981             tag: 'iframe',
12982             cls: 'form-control', // bootstrap..
12983             id: this.frameId,
12984             name: this.frameId,
12985             frameBorder : 'no',
12986             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
12987         }, this.el
12988         );
12989         
12990         
12991         this.iframe = iframe.dom;
12992
12993          this.assignDocWin();
12994         
12995         this.doc.designMode = 'on';
12996        
12997         this.doc.open();
12998         this.doc.write(this.getDocMarkup());
12999         this.doc.close();
13000
13001         
13002         var task = { // must defer to wait for browser to be ready
13003             run : function(){
13004                 //console.log("run task?" + this.doc.readyState);
13005                 this.assignDocWin();
13006                 if(this.doc.body || this.doc.readyState == 'complete'){
13007                     try {
13008                         this.doc.designMode="on";
13009                     } catch (e) {
13010                         return;
13011                     }
13012                     Roo.TaskMgr.stop(task);
13013                     this.initEditor.defer(10, this);
13014                 }
13015             },
13016             interval : 10,
13017             duration: 10000,
13018             scope: this
13019         };
13020         Roo.TaskMgr.start(task);
13021
13022         
13023          
13024     },
13025
13026     // private
13027     onResize : function(w, h)
13028     {
13029          Roo.log('resize: ' +w + ',' + h );
13030         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13031         if(!this.iframe){
13032             return;
13033         }
13034         if(typeof w == 'number'){
13035             
13036             this.iframe.style.width = w + 'px';
13037         }
13038         if(typeof h == 'number'){
13039             
13040             this.iframe.style.height = h + 'px';
13041             if(this.doc){
13042                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13043             }
13044         }
13045         
13046     },
13047
13048     /**
13049      * Toggles the editor between standard and source edit mode.
13050      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13051      */
13052     toggleSourceEdit : function(sourceEditMode){
13053         
13054         this.sourceEditMode = sourceEditMode === true;
13055         
13056         if(this.sourceEditMode){
13057  
13058             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13059             
13060         }else{
13061             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13062             //this.iframe.className = '';
13063             this.deferFocus();
13064         }
13065         //this.setSize(this.owner.wrap.getSize());
13066         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13067     },
13068
13069     
13070   
13071
13072     /**
13073      * Protected method that will not generally be called directly. If you need/want
13074      * custom HTML cleanup, this is the method you should override.
13075      * @param {String} html The HTML to be cleaned
13076      * return {String} The cleaned HTML
13077      */
13078     cleanHtml : function(html){
13079         html = String(html);
13080         if(html.length > 5){
13081             if(Roo.isSafari){ // strip safari nonsense
13082                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13083             }
13084         }
13085         if(html == '&nbsp;'){
13086             html = '';
13087         }
13088         return html;
13089     },
13090
13091     /**
13092      * HTML Editor -> Textarea
13093      * Protected method that will not generally be called directly. Syncs the contents
13094      * of the editor iframe with the textarea.
13095      */
13096     syncValue : function(){
13097         if(this.initialized){
13098             var bd = (this.doc.body || this.doc.documentElement);
13099             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13100             var html = bd.innerHTML;
13101             if(Roo.isSafari){
13102                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13103                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13104                 if(m && m[1]){
13105                     html = '<div style="'+m[0]+'">' + html + '</div>';
13106                 }
13107             }
13108             html = this.cleanHtml(html);
13109             // fix up the special chars.. normaly like back quotes in word...
13110             // however we do not want to do this with chinese..
13111             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13112                 var cc = b.charCodeAt();
13113                 if (
13114                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13115                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13116                     (cc >= 0xf900 && cc < 0xfb00 )
13117                 ) {
13118                         return b;
13119                 }
13120                 return "&#"+cc+";" 
13121             });
13122             if(this.owner.fireEvent('beforesync', this, html) !== false){
13123                 this.el.dom.value = html;
13124                 this.owner.fireEvent('sync', this, html);
13125             }
13126         }
13127     },
13128
13129     /**
13130      * Protected method that will not generally be called directly. Pushes the value of the textarea
13131      * into the iframe editor.
13132      */
13133     pushValue : function(){
13134         if(this.initialized){
13135             var v = this.el.dom.value.trim();
13136             
13137 //            if(v.length < 1){
13138 //                v = '&#160;';
13139 //            }
13140             
13141             if(this.owner.fireEvent('beforepush', this, v) !== false){
13142                 var d = (this.doc.body || this.doc.documentElement);
13143                 d.innerHTML = v;
13144                 this.cleanUpPaste();
13145                 this.el.dom.value = d.innerHTML;
13146                 this.owner.fireEvent('push', this, v);
13147             }
13148         }
13149     },
13150
13151     // private
13152     deferFocus : function(){
13153         this.focus.defer(10, this);
13154     },
13155
13156     // doc'ed in Field
13157     focus : function(){
13158         if(this.win && !this.sourceEditMode){
13159             this.win.focus();
13160         }else{
13161             this.el.focus();
13162         }
13163     },
13164     
13165     assignDocWin: function()
13166     {
13167         var iframe = this.iframe;
13168         
13169          if(Roo.isIE){
13170             this.doc = iframe.contentWindow.document;
13171             this.win = iframe.contentWindow;
13172         } else {
13173             if (!Roo.get(this.frameId)) {
13174                 return;
13175             }
13176             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13177             this.win = Roo.get(this.frameId).dom.contentWindow;
13178         }
13179     },
13180     
13181     // private
13182     initEditor : function(){
13183         //console.log("INIT EDITOR");
13184         this.assignDocWin();
13185         
13186         
13187         
13188         this.doc.designMode="on";
13189         this.doc.open();
13190         this.doc.write(this.getDocMarkup());
13191         this.doc.close();
13192         
13193         var dbody = (this.doc.body || this.doc.documentElement);
13194         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13195         // this copies styles from the containing element into thsi one..
13196         // not sure why we need all of this..
13197         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13198         ss['background-attachment'] = 'fixed'; // w3c
13199         dbody.bgProperties = 'fixed'; // ie
13200         Roo.DomHelper.applyStyles(dbody, ss);
13201         Roo.EventManager.on(this.doc, {
13202             //'mousedown': this.onEditorEvent,
13203             'mouseup': this.onEditorEvent,
13204             'dblclick': this.onEditorEvent,
13205             'click': this.onEditorEvent,
13206             'keyup': this.onEditorEvent,
13207             buffer:100,
13208             scope: this
13209         });
13210         if(Roo.isGecko){
13211             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13212         }
13213         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13214             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13215         }
13216         this.initialized = true;
13217
13218         this.owner.fireEvent('initialize', this);
13219         this.pushValue();
13220     },
13221
13222     // private
13223     onDestroy : function(){
13224         
13225         
13226         
13227         if(this.rendered){
13228             
13229             //for (var i =0; i < this.toolbars.length;i++) {
13230             //    // fixme - ask toolbars for heights?
13231             //    this.toolbars[i].onDestroy();
13232            // }
13233             
13234             //this.wrap.dom.innerHTML = '';
13235             //this.wrap.remove();
13236         }
13237     },
13238
13239     // private
13240     onFirstFocus : function(){
13241         
13242         this.assignDocWin();
13243         
13244         
13245         this.activated = true;
13246          
13247     
13248         if(Roo.isGecko){ // prevent silly gecko errors
13249             this.win.focus();
13250             var s = this.win.getSelection();
13251             if(!s.focusNode || s.focusNode.nodeType != 3){
13252                 var r = s.getRangeAt(0);
13253                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13254                 r.collapse(true);
13255                 this.deferFocus();
13256             }
13257             try{
13258                 this.execCmd('useCSS', true);
13259                 this.execCmd('styleWithCSS', false);
13260             }catch(e){}
13261         }
13262         this.owner.fireEvent('activate', this);
13263     },
13264
13265     // private
13266     adjustFont: function(btn){
13267         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13268         //if(Roo.isSafari){ // safari
13269         //    adjust *= 2;
13270        // }
13271         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13272         if(Roo.isSafari){ // safari
13273             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13274             v =  (v < 10) ? 10 : v;
13275             v =  (v > 48) ? 48 : v;
13276             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13277             
13278         }
13279         
13280         
13281         v = Math.max(1, v+adjust);
13282         
13283         this.execCmd('FontSize', v  );
13284     },
13285
13286     onEditorEvent : function(e){
13287         this.owner.fireEvent('editorevent', this, e);
13288       //  this.updateToolbar();
13289         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13290     },
13291
13292     insertTag : function(tg)
13293     {
13294         // could be a bit smarter... -> wrap the current selected tRoo..
13295         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13296             
13297             range = this.createRange(this.getSelection());
13298             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13299             wrappingNode.appendChild(range.extractContents());
13300             range.insertNode(wrappingNode);
13301
13302             return;
13303             
13304             
13305             
13306         }
13307         this.execCmd("formatblock",   tg);
13308         
13309     },
13310     
13311     insertText : function(txt)
13312     {
13313         
13314         
13315         var range = this.createRange();
13316         range.deleteContents();
13317                //alert(Sender.getAttribute('label'));
13318                
13319         range.insertNode(this.doc.createTextNode(txt));
13320     } ,
13321     
13322      
13323
13324     /**
13325      * Executes a Midas editor command on the editor document and performs necessary focus and
13326      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13327      * @param {String} cmd The Midas command
13328      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13329      */
13330     relayCmd : function(cmd, value){
13331         this.win.focus();
13332         this.execCmd(cmd, value);
13333         this.owner.fireEvent('editorevent', this);
13334         //this.updateToolbar();
13335         this.owner.deferFocus();
13336     },
13337
13338     /**
13339      * Executes a Midas editor command directly on the editor document.
13340      * For visual commands, you should use {@link #relayCmd} instead.
13341      * <b>This should only be called after the editor is initialized.</b>
13342      * @param {String} cmd The Midas command
13343      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13344      */
13345     execCmd : function(cmd, value){
13346         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13347         this.syncValue();
13348     },
13349  
13350  
13351    
13352     /**
13353      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13354      * to insert tRoo.
13355      * @param {String} text | dom node.. 
13356      */
13357     insertAtCursor : function(text)
13358     {
13359         
13360         
13361         
13362         if(!this.activated){
13363             return;
13364         }
13365         /*
13366         if(Roo.isIE){
13367             this.win.focus();
13368             var r = this.doc.selection.createRange();
13369             if(r){
13370                 r.collapse(true);
13371                 r.pasteHTML(text);
13372                 this.syncValue();
13373                 this.deferFocus();
13374             
13375             }
13376             return;
13377         }
13378         */
13379         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13380             this.win.focus();
13381             
13382             
13383             // from jquery ui (MIT licenced)
13384             var range, node;
13385             var win = this.win;
13386             
13387             if (win.getSelection && win.getSelection().getRangeAt) {
13388                 range = win.getSelection().getRangeAt(0);
13389                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13390                 range.insertNode(node);
13391             } else if (win.document.selection && win.document.selection.createRange) {
13392                 // no firefox support
13393                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13394                 win.document.selection.createRange().pasteHTML(txt);
13395             } else {
13396                 // no firefox support
13397                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13398                 this.execCmd('InsertHTML', txt);
13399             } 
13400             
13401             this.syncValue();
13402             
13403             this.deferFocus();
13404         }
13405     },
13406  // private
13407     mozKeyPress : function(e){
13408         if(e.ctrlKey){
13409             var c = e.getCharCode(), cmd;
13410           
13411             if(c > 0){
13412                 c = String.fromCharCode(c).toLowerCase();
13413                 switch(c){
13414                     case 'b':
13415                         cmd = 'bold';
13416                         break;
13417                     case 'i':
13418                         cmd = 'italic';
13419                         break;
13420                     
13421                     case 'u':
13422                         cmd = 'underline';
13423                         break;
13424                     
13425                     case 'v':
13426                         this.cleanUpPaste.defer(100, this);
13427                         return;
13428                         
13429                 }
13430                 if(cmd){
13431                     this.win.focus();
13432                     this.execCmd(cmd);
13433                     this.deferFocus();
13434                     e.preventDefault();
13435                 }
13436                 
13437             }
13438         }
13439     },
13440
13441     // private
13442     fixKeys : function(){ // load time branching for fastest keydown performance
13443         if(Roo.isIE){
13444             return function(e){
13445                 var k = e.getKey(), r;
13446                 if(k == e.TAB){
13447                     e.stopEvent();
13448                     r = this.doc.selection.createRange();
13449                     if(r){
13450                         r.collapse(true);
13451                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13452                         this.deferFocus();
13453                     }
13454                     return;
13455                 }
13456                 
13457                 if(k == e.ENTER){
13458                     r = this.doc.selection.createRange();
13459                     if(r){
13460                         var target = r.parentElement();
13461                         if(!target || target.tagName.toLowerCase() != 'li'){
13462                             e.stopEvent();
13463                             r.pasteHTML('<br />');
13464                             r.collapse(false);
13465                             r.select();
13466                         }
13467                     }
13468                 }
13469                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13470                     this.cleanUpPaste.defer(100, this);
13471                     return;
13472                 }
13473                 
13474                 
13475             };
13476         }else if(Roo.isOpera){
13477             return function(e){
13478                 var k = e.getKey();
13479                 if(k == e.TAB){
13480                     e.stopEvent();
13481                     this.win.focus();
13482                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13483                     this.deferFocus();
13484                 }
13485                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13486                     this.cleanUpPaste.defer(100, this);
13487                     return;
13488                 }
13489                 
13490             };
13491         }else if(Roo.isSafari){
13492             return function(e){
13493                 var k = e.getKey();
13494                 
13495                 if(k == e.TAB){
13496                     e.stopEvent();
13497                     this.execCmd('InsertText','\t');
13498                     this.deferFocus();
13499                     return;
13500                 }
13501                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13502                     this.cleanUpPaste.defer(100, this);
13503                     return;
13504                 }
13505                 
13506              };
13507         }
13508     }(),
13509     
13510     getAllAncestors: function()
13511     {
13512         var p = this.getSelectedNode();
13513         var a = [];
13514         if (!p) {
13515             a.push(p); // push blank onto stack..
13516             p = this.getParentElement();
13517         }
13518         
13519         
13520         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13521             a.push(p);
13522             p = p.parentNode;
13523         }
13524         a.push(this.doc.body);
13525         return a;
13526     },
13527     lastSel : false,
13528     lastSelNode : false,
13529     
13530     
13531     getSelection : function() 
13532     {
13533         this.assignDocWin();
13534         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13535     },
13536     
13537     getSelectedNode: function() 
13538     {
13539         // this may only work on Gecko!!!
13540         
13541         // should we cache this!!!!
13542         
13543         
13544         
13545          
13546         var range = this.createRange(this.getSelection()).cloneRange();
13547         
13548         if (Roo.isIE) {
13549             var parent = range.parentElement();
13550             while (true) {
13551                 var testRange = range.duplicate();
13552                 testRange.moveToElementText(parent);
13553                 if (testRange.inRange(range)) {
13554                     break;
13555                 }
13556                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13557                     break;
13558                 }
13559                 parent = parent.parentElement;
13560             }
13561             return parent;
13562         }
13563         
13564         // is ancestor a text element.
13565         var ac =  range.commonAncestorContainer;
13566         if (ac.nodeType == 3) {
13567             ac = ac.parentNode;
13568         }
13569         
13570         var ar = ac.childNodes;
13571          
13572         var nodes = [];
13573         var other_nodes = [];
13574         var has_other_nodes = false;
13575         for (var i=0;i<ar.length;i++) {
13576             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13577                 continue;
13578             }
13579             // fullly contained node.
13580             
13581             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13582                 nodes.push(ar[i]);
13583                 continue;
13584             }
13585             
13586             // probably selected..
13587             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13588                 other_nodes.push(ar[i]);
13589                 continue;
13590             }
13591             // outer..
13592             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13593                 continue;
13594             }
13595             
13596             
13597             has_other_nodes = true;
13598         }
13599         if (!nodes.length && other_nodes.length) {
13600             nodes= other_nodes;
13601         }
13602         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13603             return false;
13604         }
13605         
13606         return nodes[0];
13607     },
13608     createRange: function(sel)
13609     {
13610         // this has strange effects when using with 
13611         // top toolbar - not sure if it's a great idea.
13612         //this.editor.contentWindow.focus();
13613         if (typeof sel != "undefined") {
13614             try {
13615                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13616             } catch(e) {
13617                 return this.doc.createRange();
13618             }
13619         } else {
13620             return this.doc.createRange();
13621         }
13622     },
13623     getParentElement: function()
13624     {
13625         
13626         this.assignDocWin();
13627         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13628         
13629         var range = this.createRange(sel);
13630          
13631         try {
13632             var p = range.commonAncestorContainer;
13633             while (p.nodeType == 3) { // text node
13634                 p = p.parentNode;
13635             }
13636             return p;
13637         } catch (e) {
13638             return null;
13639         }
13640     
13641     },
13642     /***
13643      *
13644      * Range intersection.. the hard stuff...
13645      *  '-1' = before
13646      *  '0' = hits..
13647      *  '1' = after.
13648      *         [ -- selected range --- ]
13649      *   [fail]                        [fail]
13650      *
13651      *    basically..
13652      *      if end is before start or  hits it. fail.
13653      *      if start is after end or hits it fail.
13654      *
13655      *   if either hits (but other is outside. - then it's not 
13656      *   
13657      *    
13658      **/
13659     
13660     
13661     // @see http://www.thismuchiknow.co.uk/?p=64.
13662     rangeIntersectsNode : function(range, node)
13663     {
13664         var nodeRange = node.ownerDocument.createRange();
13665         try {
13666             nodeRange.selectNode(node);
13667         } catch (e) {
13668             nodeRange.selectNodeContents(node);
13669         }
13670     
13671         var rangeStartRange = range.cloneRange();
13672         rangeStartRange.collapse(true);
13673     
13674         var rangeEndRange = range.cloneRange();
13675         rangeEndRange.collapse(false);
13676     
13677         var nodeStartRange = nodeRange.cloneRange();
13678         nodeStartRange.collapse(true);
13679     
13680         var nodeEndRange = nodeRange.cloneRange();
13681         nodeEndRange.collapse(false);
13682     
13683         return rangeStartRange.compareBoundaryPoints(
13684                  Range.START_TO_START, nodeEndRange) == -1 &&
13685                rangeEndRange.compareBoundaryPoints(
13686                  Range.START_TO_START, nodeStartRange) == 1;
13687         
13688          
13689     },
13690     rangeCompareNode : function(range, node)
13691     {
13692         var nodeRange = node.ownerDocument.createRange();
13693         try {
13694             nodeRange.selectNode(node);
13695         } catch (e) {
13696             nodeRange.selectNodeContents(node);
13697         }
13698         
13699         
13700         range.collapse(true);
13701     
13702         nodeRange.collapse(true);
13703      
13704         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13705         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13706          
13707         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13708         
13709         var nodeIsBefore   =  ss == 1;
13710         var nodeIsAfter    = ee == -1;
13711         
13712         if (nodeIsBefore && nodeIsAfter)
13713             return 0; // outer
13714         if (!nodeIsBefore && nodeIsAfter)
13715             return 1; //right trailed.
13716         
13717         if (nodeIsBefore && !nodeIsAfter)
13718             return 2;  // left trailed.
13719         // fully contined.
13720         return 3;
13721     },
13722
13723     // private? - in a new class?
13724     cleanUpPaste :  function()
13725     {
13726         // cleans up the whole document..
13727          Roo.log('cleanuppaste');
13728         this.cleanUpChildren(this.doc.body);
13729         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13730         if (clean != this.doc.body.innerHTML) {
13731             this.doc.body.innerHTML = clean;
13732         }
13733         
13734     },
13735     
13736     cleanWordChars : function(input) {// change the chars to hex code
13737         var he = Roo.HtmlEditorCore;
13738         
13739         var output = input;
13740         Roo.each(he.swapCodes, function(sw) { 
13741             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13742             
13743             output = output.replace(swapper, sw[1]);
13744         });
13745         
13746         return output;
13747     },
13748     
13749     
13750     cleanUpChildren : function (n)
13751     {
13752         if (!n.childNodes.length) {
13753             return;
13754         }
13755         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13756            this.cleanUpChild(n.childNodes[i]);
13757         }
13758     },
13759     
13760     
13761         
13762     
13763     cleanUpChild : function (node)
13764     {
13765         var ed = this;
13766         //console.log(node);
13767         if (node.nodeName == "#text") {
13768             // clean up silly Windows -- stuff?
13769             return; 
13770         }
13771         if (node.nodeName == "#comment") {
13772             node.parentNode.removeChild(node);
13773             // clean up silly Windows -- stuff?
13774             return; 
13775         }
13776         
13777         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
13778             // remove node.
13779             node.parentNode.removeChild(node);
13780             return;
13781             
13782         }
13783         
13784         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13785         
13786         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13787         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13788         
13789         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13790         //    remove_keep_children = true;
13791         //}
13792         
13793         if (remove_keep_children) {
13794             this.cleanUpChildren(node);
13795             // inserts everything just before this node...
13796             while (node.childNodes.length) {
13797                 var cn = node.childNodes[0];
13798                 node.removeChild(cn);
13799                 node.parentNode.insertBefore(cn, node);
13800             }
13801             node.parentNode.removeChild(node);
13802             return;
13803         }
13804         
13805         if (!node.attributes || !node.attributes.length) {
13806             this.cleanUpChildren(node);
13807             return;
13808         }
13809         
13810         function cleanAttr(n,v)
13811         {
13812             
13813             if (v.match(/^\./) || v.match(/^\//)) {
13814                 return;
13815             }
13816             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13817                 return;
13818             }
13819             if (v.match(/^#/)) {
13820                 return;
13821             }
13822 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13823             node.removeAttribute(n);
13824             
13825         }
13826         
13827         function cleanStyle(n,v)
13828         {
13829             if (v.match(/expression/)) { //XSS?? should we even bother..
13830                 node.removeAttribute(n);
13831                 return;
13832             }
13833             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13834             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13835             
13836             
13837             var parts = v.split(/;/);
13838             var clean = [];
13839             
13840             Roo.each(parts, function(p) {
13841                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13842                 if (!p.length) {
13843                     return true;
13844                 }
13845                 var l = p.split(':').shift().replace(/\s+/g,'');
13846                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13847                 
13848                 
13849                 if ( cblack.indexOf(l) > -1) {
13850 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13851                     //node.removeAttribute(n);
13852                     return true;
13853                 }
13854                 //Roo.log()
13855                 // only allow 'c whitelisted system attributes'
13856                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13857 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13858                     //node.removeAttribute(n);
13859                     return true;
13860                 }
13861                 
13862                 
13863                  
13864                 
13865                 clean.push(p);
13866                 return true;
13867             });
13868             if (clean.length) { 
13869                 node.setAttribute(n, clean.join(';'));
13870             } else {
13871                 node.removeAttribute(n);
13872             }
13873             
13874         }
13875         
13876         
13877         for (var i = node.attributes.length-1; i > -1 ; i--) {
13878             var a = node.attributes[i];
13879             //console.log(a);
13880             
13881             if (a.name.toLowerCase().substr(0,2)=='on')  {
13882                 node.removeAttribute(a.name);
13883                 continue;
13884             }
13885             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13886                 node.removeAttribute(a.name);
13887                 continue;
13888             }
13889             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13890                 cleanAttr(a.name,a.value); // fixme..
13891                 continue;
13892             }
13893             if (a.name == 'style') {
13894                 cleanStyle(a.name,a.value);
13895                 continue;
13896             }
13897             /// clean up MS crap..
13898             // tecnically this should be a list of valid class'es..
13899             
13900             
13901             if (a.name == 'class') {
13902                 if (a.value.match(/^Mso/)) {
13903                     node.className = '';
13904                 }
13905                 
13906                 if (a.value.match(/body/)) {
13907                     node.className = '';
13908                 }
13909                 continue;
13910             }
13911             
13912             // style cleanup!?
13913             // class cleanup?
13914             
13915         }
13916         
13917         
13918         this.cleanUpChildren(node);
13919         
13920         
13921     }
13922     
13923     
13924     // hide stuff that is not compatible
13925     /**
13926      * @event blur
13927      * @hide
13928      */
13929     /**
13930      * @event change
13931      * @hide
13932      */
13933     /**
13934      * @event focus
13935      * @hide
13936      */
13937     /**
13938      * @event specialkey
13939      * @hide
13940      */
13941     /**
13942      * @cfg {String} fieldClass @hide
13943      */
13944     /**
13945      * @cfg {String} focusClass @hide
13946      */
13947     /**
13948      * @cfg {String} autoCreate @hide
13949      */
13950     /**
13951      * @cfg {String} inputType @hide
13952      */
13953     /**
13954      * @cfg {String} invalidClass @hide
13955      */
13956     /**
13957      * @cfg {String} invalidText @hide
13958      */
13959     /**
13960      * @cfg {String} msgFx @hide
13961      */
13962     /**
13963      * @cfg {String} validateOnBlur @hide
13964      */
13965 });
13966
13967 Roo.HtmlEditorCore.white = [
13968         'area', 'br', 'img', 'input', 'hr', 'wbr',
13969         
13970        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
13971        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
13972        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
13973        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
13974        'table',   'ul',         'xmp', 
13975        
13976        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
13977       'thead',   'tr', 
13978      
13979       'dir', 'menu', 'ol', 'ul', 'dl',
13980        
13981       'embed',  'object'
13982 ];
13983
13984
13985 Roo.HtmlEditorCore.black = [
13986     //    'embed',  'object', // enable - backend responsiblity to clean thiese
13987         'applet', // 
13988         'base',   'basefont', 'bgsound', 'blink',  'body', 
13989         'frame',  'frameset', 'head',    'html',   'ilayer', 
13990         'iframe', 'layer',  'link',     'meta',    'object',   
13991         'script', 'style' ,'title',  'xml' // clean later..
13992 ];
13993 Roo.HtmlEditorCore.clean = [
13994     'script', 'style', 'title', 'xml'
13995 ];
13996 Roo.HtmlEditorCore.remove = [
13997     'font'
13998 ];
13999 // attributes..
14000
14001 Roo.HtmlEditorCore.ablack = [
14002     'on'
14003 ];
14004     
14005 Roo.HtmlEditorCore.aclean = [ 
14006     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14007 ];
14008
14009 // protocols..
14010 Roo.HtmlEditorCore.pwhite= [
14011         'http',  'https',  'mailto'
14012 ];
14013
14014 // white listed style attributes.
14015 Roo.HtmlEditorCore.cwhite= [
14016       //  'text-align', /// default is to allow most things..
14017       
14018          
14019 //        'font-size'//??
14020 ];
14021
14022 // black listed style attributes.
14023 Roo.HtmlEditorCore.cblack= [
14024       //  'font-size' -- this can be set by the project 
14025 ];
14026
14027
14028 Roo.HtmlEditorCore.swapCodes   =[ 
14029     [    8211, "--" ], 
14030     [    8212, "--" ], 
14031     [    8216,  "'" ],  
14032     [    8217, "'" ],  
14033     [    8220, '"' ],  
14034     [    8221, '"' ],  
14035     [    8226, "*" ],  
14036     [    8230, "..." ]
14037 ]; 
14038
14039     /*
14040  * - LGPL
14041  *
14042  * HtmlEditor
14043  * 
14044  */
14045
14046 /**
14047  * @class Roo.bootstrap.HtmlEditor
14048  * @extends Roo.bootstrap.TextArea
14049  * Bootstrap HtmlEditor class
14050
14051  * @constructor
14052  * Create a new HtmlEditor
14053  * @param {Object} config The config object
14054  */
14055
14056 Roo.bootstrap.HtmlEditor = function(config){
14057     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14058     if (!this.toolbars) {
14059         this.toolbars = [];
14060     }
14061     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14062     this.addEvents({
14063             /**
14064              * @event initialize
14065              * Fires when the editor is fully initialized (including the iframe)
14066              * @param {HtmlEditor} this
14067              */
14068             initialize: true,
14069             /**
14070              * @event activate
14071              * Fires when the editor is first receives the focus. Any insertion must wait
14072              * until after this event.
14073              * @param {HtmlEditor} this
14074              */
14075             activate: true,
14076              /**
14077              * @event beforesync
14078              * Fires before the textarea is updated with content from the editor iframe. Return false
14079              * to cancel the sync.
14080              * @param {HtmlEditor} this
14081              * @param {String} html
14082              */
14083             beforesync: true,
14084              /**
14085              * @event beforepush
14086              * Fires before the iframe editor is updated with content from the textarea. Return false
14087              * to cancel the push.
14088              * @param {HtmlEditor} this
14089              * @param {String} html
14090              */
14091             beforepush: true,
14092              /**
14093              * @event sync
14094              * Fires when the textarea is updated with content from the editor iframe.
14095              * @param {HtmlEditor} this
14096              * @param {String} html
14097              */
14098             sync: true,
14099              /**
14100              * @event push
14101              * Fires when the iframe editor is updated with content from the textarea.
14102              * @param {HtmlEditor} this
14103              * @param {String} html
14104              */
14105             push: true,
14106              /**
14107              * @event editmodechange
14108              * Fires when the editor switches edit modes
14109              * @param {HtmlEditor} this
14110              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14111              */
14112             editmodechange: true,
14113             /**
14114              * @event editorevent
14115              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14116              * @param {HtmlEditor} this
14117              */
14118             editorevent: true,
14119             /**
14120              * @event firstfocus
14121              * Fires when on first focus - needed by toolbars..
14122              * @param {HtmlEditor} this
14123              */
14124             firstfocus: true,
14125             /**
14126              * @event autosave
14127              * Auto save the htmlEditor value as a file into Events
14128              * @param {HtmlEditor} this
14129              */
14130             autosave: true,
14131             /**
14132              * @event savedpreview
14133              * preview the saved version of htmlEditor
14134              * @param {HtmlEditor} this
14135              */
14136             savedpreview: true
14137         });
14138 };
14139
14140
14141 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14142     
14143     
14144       /**
14145      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14146      */
14147     toolbars : false,
14148    
14149      /**
14150      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14151      *                        Roo.resizable.
14152      */
14153     resizable : false,
14154      /**
14155      * @cfg {Number} height (in pixels)
14156      */   
14157     height: 300,
14158    /**
14159      * @cfg {Number} width (in pixels)
14160      */   
14161     width: false,
14162     
14163     /**
14164      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14165      * 
14166      */
14167     stylesheets: false,
14168     
14169     // id of frame..
14170     frameId: false,
14171     
14172     // private properties
14173     validationEvent : false,
14174     deferHeight: true,
14175     initialized : false,
14176     activated : false,
14177     
14178     onFocus : Roo.emptyFn,
14179     iframePad:3,
14180     hideMode:'offsets',
14181     
14182     
14183     tbContainer : false,
14184     
14185     toolbarContainer :function() {
14186         return this.wrap.select('.x-html-editor-tb',true).first();
14187     },
14188
14189     /**
14190      * Protected method that will not generally be called directly. It
14191      * is called when the editor creates its toolbar. Override this method if you need to
14192      * add custom toolbar buttons.
14193      * @param {HtmlEditor} editor
14194      */
14195     createToolbar : function(){
14196         
14197         Roo.log("create toolbars");
14198         
14199         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14200         this.toolbars[0].render(this.toolbarContainer());
14201         
14202         return;
14203         
14204 //        if (!editor.toolbars || !editor.toolbars.length) {
14205 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14206 //        }
14207 //        
14208 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14209 //            editor.toolbars[i] = Roo.factory(
14210 //                    typeof(editor.toolbars[i]) == 'string' ?
14211 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14212 //                Roo.bootstrap.HtmlEditor);
14213 //            editor.toolbars[i].init(editor);
14214 //        }
14215     },
14216
14217      
14218     // private
14219     onRender : function(ct, position)
14220     {
14221        // Roo.log("Call onRender: " + this.xtype);
14222         var _t = this;
14223         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14224       
14225         this.wrap = this.inputEl().wrap({
14226             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14227         });
14228         
14229         this.editorcore.onRender(ct, position);
14230          
14231         if (this.resizable) {
14232             this.resizeEl = new Roo.Resizable(this.wrap, {
14233                 pinned : true,
14234                 wrap: true,
14235                 dynamic : true,
14236                 minHeight : this.height,
14237                 height: this.height,
14238                 handles : this.resizable,
14239                 width: this.width,
14240                 listeners : {
14241                     resize : function(r, w, h) {
14242                         _t.onResize(w,h); // -something
14243                     }
14244                 }
14245             });
14246             
14247         }
14248         this.createToolbar(this);
14249        
14250         
14251         if(!this.width && this.resizable){
14252             this.setSize(this.wrap.getSize());
14253         }
14254         if (this.resizeEl) {
14255             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14256             // should trigger onReize..
14257         }
14258         
14259     },
14260
14261     // private
14262     onResize : function(w, h)
14263     {
14264         Roo.log('resize: ' +w + ',' + h );
14265         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14266         var ew = false;
14267         var eh = false;
14268         
14269         if(this.inputEl() ){
14270             if(typeof w == 'number'){
14271                 var aw = w - this.wrap.getFrameWidth('lr');
14272                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14273                 ew = aw;
14274             }
14275             if(typeof h == 'number'){
14276                  var tbh = -11;  // fixme it needs to tool bar size!
14277                 for (var i =0; i < this.toolbars.length;i++) {
14278                     // fixme - ask toolbars for heights?
14279                     tbh += this.toolbars[i].el.getHeight();
14280                     //if (this.toolbars[i].footer) {
14281                     //    tbh += this.toolbars[i].footer.el.getHeight();
14282                     //}
14283                 }
14284               
14285                 
14286                 
14287                 
14288                 
14289                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14290                 ah -= 5; // knock a few pixes off for look..
14291                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14292                 var eh = ah;
14293             }
14294         }
14295         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14296         this.editorcore.onResize(ew,eh);
14297         
14298     },
14299
14300     /**
14301      * Toggles the editor between standard and source edit mode.
14302      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14303      */
14304     toggleSourceEdit : function(sourceEditMode)
14305     {
14306         this.editorcore.toggleSourceEdit(sourceEditMode);
14307         
14308         if(this.editorcore.sourceEditMode){
14309             Roo.log('editor - showing textarea');
14310             
14311 //            Roo.log('in');
14312 //            Roo.log(this.syncValue());
14313             this.syncValue();
14314             this.inputEl().removeClass('hide');
14315             this.inputEl().dom.removeAttribute('tabIndex');
14316             this.inputEl().focus();
14317         }else{
14318             Roo.log('editor - hiding textarea');
14319 //            Roo.log('out')
14320 //            Roo.log(this.pushValue()); 
14321             this.pushValue();
14322             
14323             this.inputEl().addClass('hide');
14324             this.inputEl().dom.setAttribute('tabIndex', -1);
14325             //this.deferFocus();
14326         }
14327          
14328         if(this.resizable){
14329             this.setSize(this.wrap.getSize());
14330         }
14331         
14332         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14333     },
14334  
14335     // private (for BoxComponent)
14336     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14337
14338     // private (for BoxComponent)
14339     getResizeEl : function(){
14340         return this.wrap;
14341     },
14342
14343     // private (for BoxComponent)
14344     getPositionEl : function(){
14345         return this.wrap;
14346     },
14347
14348     // private
14349     initEvents : function(){
14350         this.originalValue = this.getValue();
14351     },
14352
14353 //    /**
14354 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14355 //     * @method
14356 //     */
14357 //    markInvalid : Roo.emptyFn,
14358 //    /**
14359 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14360 //     * @method
14361 //     */
14362 //    clearInvalid : Roo.emptyFn,
14363
14364     setValue : function(v){
14365         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14366         this.editorcore.pushValue();
14367     },
14368
14369      
14370     // private
14371     deferFocus : function(){
14372         this.focus.defer(10, this);
14373     },
14374
14375     // doc'ed in Field
14376     focus : function(){
14377         this.editorcore.focus();
14378         
14379     },
14380       
14381
14382     // private
14383     onDestroy : function(){
14384         
14385         
14386         
14387         if(this.rendered){
14388             
14389             for (var i =0; i < this.toolbars.length;i++) {
14390                 // fixme - ask toolbars for heights?
14391                 this.toolbars[i].onDestroy();
14392             }
14393             
14394             this.wrap.dom.innerHTML = '';
14395             this.wrap.remove();
14396         }
14397     },
14398
14399     // private
14400     onFirstFocus : function(){
14401         //Roo.log("onFirstFocus");
14402         this.editorcore.onFirstFocus();
14403          for (var i =0; i < this.toolbars.length;i++) {
14404             this.toolbars[i].onFirstFocus();
14405         }
14406         
14407     },
14408     
14409     // private
14410     syncValue : function()
14411     {   
14412         this.editorcore.syncValue();
14413     },
14414     
14415     pushValue : function()
14416     {   
14417         this.editorcore.pushValue();
14418     }
14419      
14420     
14421     // hide stuff that is not compatible
14422     /**
14423      * @event blur
14424      * @hide
14425      */
14426     /**
14427      * @event change
14428      * @hide
14429      */
14430     /**
14431      * @event focus
14432      * @hide
14433      */
14434     /**
14435      * @event specialkey
14436      * @hide
14437      */
14438     /**
14439      * @cfg {String} fieldClass @hide
14440      */
14441     /**
14442      * @cfg {String} focusClass @hide
14443      */
14444     /**
14445      * @cfg {String} autoCreate @hide
14446      */
14447     /**
14448      * @cfg {String} inputType @hide
14449      */
14450     /**
14451      * @cfg {String} invalidClass @hide
14452      */
14453     /**
14454      * @cfg {String} invalidText @hide
14455      */
14456     /**
14457      * @cfg {String} msgFx @hide
14458      */
14459     /**
14460      * @cfg {String} validateOnBlur @hide
14461      */
14462 });
14463  
14464     
14465    
14466    
14467    
14468       
14469
14470 /**
14471  * @class Roo.bootstrap.HtmlEditorToolbar1
14472  * Basic Toolbar
14473  * 
14474  * Usage:
14475  *
14476  new Roo.bootstrap.HtmlEditor({
14477     ....
14478     toolbars : [
14479         new Roo.bootstrap.HtmlEditorToolbar1({
14480             disable : { fonts: 1 , format: 1, ..., ... , ...],
14481             btns : [ .... ]
14482         })
14483     }
14484      
14485  * 
14486  * @cfg {Object} disable List of elements to disable..
14487  * @cfg {Array} btns List of additional buttons.
14488  * 
14489  * 
14490  * NEEDS Extra CSS? 
14491  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14492  */
14493  
14494 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14495 {
14496     
14497     Roo.apply(this, config);
14498     
14499     // default disabled, based on 'good practice'..
14500     this.disable = this.disable || {};
14501     Roo.applyIf(this.disable, {
14502         fontSize : true,
14503         colors : true,
14504         specialElements : true
14505     });
14506     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14507     
14508     this.editor = config.editor;
14509     this.editorcore = config.editor.editorcore;
14510     
14511     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14512     
14513     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14514     // dont call parent... till later.
14515 }
14516 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14517     
14518     
14519     bar : true,
14520     
14521     editor : false,
14522     editorcore : false,
14523     
14524     
14525     formats : [
14526         "p" ,  
14527         "h1","h2","h3","h4","h5","h6", 
14528         "pre", "code", 
14529         "abbr", "acronym", "address", "cite", "samp", "var",
14530         'div','span'
14531     ],
14532     
14533     onRender : function(ct, position)
14534     {
14535        // Roo.log("Call onRender: " + this.xtype);
14536         
14537        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14538        Roo.log(this.el);
14539        this.el.dom.style.marginBottom = '0';
14540        var _this = this;
14541        var editorcore = this.editorcore;
14542        var editor= this.editor;
14543        
14544        var children = [];
14545        var btn = function(id,cmd , toggle, handler){
14546        
14547             var  event = toggle ? 'toggle' : 'click';
14548        
14549             var a = {
14550                 size : 'sm',
14551                 xtype: 'Button',
14552                 xns: Roo.bootstrap,
14553                 glyphicon : id,
14554                 cmd : id || cmd,
14555                 enableToggle:toggle !== false,
14556                 //html : 'submit'
14557                 pressed : toggle ? false : null,
14558                 listeners : {}
14559             }
14560             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14561                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14562             }
14563             children.push(a);
14564             return a;
14565        }
14566         
14567         var style = {
14568                 xtype: 'Button',
14569                 size : 'sm',
14570                 xns: Roo.bootstrap,
14571                 glyphicon : 'font',
14572                 //html : 'submit'
14573                 menu : {
14574                     xtype: 'Menu',
14575                     xns: Roo.bootstrap,
14576                     items:  []
14577                 }
14578         };
14579         Roo.each(this.formats, function(f) {
14580             style.menu.items.push({
14581                 xtype :'MenuItem',
14582                 xns: Roo.bootstrap,
14583                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14584                 tagname : f,
14585                 listeners : {
14586                     click : function()
14587                     {
14588                         editorcore.insertTag(this.tagname);
14589                         editor.focus();
14590                     }
14591                 }
14592                 
14593             });
14594         });
14595          children.push(style);   
14596             
14597             
14598         btn('bold',false,true);
14599         btn('italic',false,true);
14600         btn('align-left', 'justifyleft',true);
14601         btn('align-center', 'justifycenter',true);
14602         btn('align-right' , 'justifyright',true);
14603         btn('link', false, false, function(btn) {
14604             //Roo.log("create link?");
14605             var url = prompt(this.createLinkText, this.defaultLinkValue);
14606             if(url && url != 'http:/'+'/'){
14607                 this.editorcore.relayCmd('createlink', url);
14608             }
14609         }),
14610         btn('list','insertunorderedlist',true);
14611         btn('pencil', false,true, function(btn){
14612                 Roo.log(this);
14613                 
14614                 this.toggleSourceEdit(btn.pressed);
14615         });
14616         /*
14617         var cog = {
14618                 xtype: 'Button',
14619                 size : 'sm',
14620                 xns: Roo.bootstrap,
14621                 glyphicon : 'cog',
14622                 //html : 'submit'
14623                 menu : {
14624                     xtype: 'Menu',
14625                     xns: Roo.bootstrap,
14626                     items:  []
14627                 }
14628         };
14629         
14630         cog.menu.items.push({
14631             xtype :'MenuItem',
14632             xns: Roo.bootstrap,
14633             html : Clean styles,
14634             tagname : f,
14635             listeners : {
14636                 click : function()
14637                 {
14638                     editorcore.insertTag(this.tagname);
14639                     editor.focus();
14640                 }
14641             }
14642             
14643         });
14644        */
14645         
14646          
14647        this.xtype = 'Navbar';
14648         
14649         for(var i=0;i< children.length;i++) {
14650             
14651             this.buttons.add(this.addxtypeChild(children[i]));
14652             
14653         }
14654         
14655         editor.on('editorevent', this.updateToolbar, this);
14656     },
14657     onBtnClick : function(id)
14658     {
14659        this.editorcore.relayCmd(id);
14660        this.editorcore.focus();
14661     },
14662     
14663     /**
14664      * Protected method that will not generally be called directly. It triggers
14665      * a toolbar update by reading the markup state of the current selection in the editor.
14666      */
14667     updateToolbar: function(){
14668
14669         if(!this.editorcore.activated){
14670             this.editor.onFirstFocus(); // is this neeed?
14671             return;
14672         }
14673
14674         var btns = this.buttons; 
14675         var doc = this.editorcore.doc;
14676         btns.get('bold').setActive(doc.queryCommandState('bold'));
14677         btns.get('italic').setActive(doc.queryCommandState('italic'));
14678         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14679         
14680         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14681         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14682         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14683         
14684         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14685         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14686          /*
14687         
14688         var ans = this.editorcore.getAllAncestors();
14689         if (this.formatCombo) {
14690             
14691             
14692             var store = this.formatCombo.store;
14693             this.formatCombo.setValue("");
14694             for (var i =0; i < ans.length;i++) {
14695                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14696                     // select it..
14697                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14698                     break;
14699                 }
14700             }
14701         }
14702         
14703         
14704         
14705         // hides menus... - so this cant be on a menu...
14706         Roo.bootstrap.MenuMgr.hideAll();
14707         */
14708         Roo.bootstrap.MenuMgr.hideAll();
14709         //this.editorsyncValue();
14710     },
14711     onFirstFocus: function() {
14712         this.buttons.each(function(item){
14713            item.enable();
14714         });
14715     },
14716     toggleSourceEdit : function(sourceEditMode){
14717         
14718           
14719         if(sourceEditMode){
14720             Roo.log("disabling buttons");
14721            this.buttons.each( function(item){
14722                 if(item.cmd != 'pencil'){
14723                     item.disable();
14724                 }
14725             });
14726           
14727         }else{
14728             Roo.log("enabling buttons");
14729             if(this.editorcore.initialized){
14730                 this.buttons.each( function(item){
14731                     item.enable();
14732                 });
14733             }
14734             
14735         }
14736         Roo.log("calling toggole on editor");
14737         // tell the editor that it's been pressed..
14738         this.editor.toggleSourceEdit(sourceEditMode);
14739        
14740     }
14741 });
14742
14743
14744
14745
14746
14747 /**
14748  * @class Roo.bootstrap.Table.AbstractSelectionModel
14749  * @extends Roo.util.Observable
14750  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14751  * implemented by descendant classes.  This class should not be directly instantiated.
14752  * @constructor
14753  */
14754 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14755     this.locked = false;
14756     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14757 };
14758
14759
14760 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14761     /** @ignore Called by the grid automatically. Do not call directly. */
14762     init : function(grid){
14763         this.grid = grid;
14764         this.initEvents();
14765     },
14766
14767     /**
14768      * Locks the selections.
14769      */
14770     lock : function(){
14771         this.locked = true;
14772     },
14773
14774     /**
14775      * Unlocks the selections.
14776      */
14777     unlock : function(){
14778         this.locked = false;
14779     },
14780
14781     /**
14782      * Returns true if the selections are locked.
14783      * @return {Boolean}
14784      */
14785     isLocked : function(){
14786         return this.locked;
14787     }
14788 });
14789 /**
14790  * @class Roo.bootstrap.Table.ColumnModel
14791  * @extends Roo.util.Observable
14792  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14793  * the columns in the table.
14794  
14795  * @constructor
14796  * @param {Object} config An Array of column config objects. See this class's
14797  * config objects for details.
14798 */
14799 Roo.bootstrap.Table.ColumnModel = function(config){
14800         /**
14801      * The config passed into the constructor
14802      */
14803     this.config = config;
14804     this.lookup = {};
14805
14806     // if no id, create one
14807     // if the column does not have a dataIndex mapping,
14808     // map it to the order it is in the config
14809     for(var i = 0, len = config.length; i < len; i++){
14810         var c = config[i];
14811         if(typeof c.dataIndex == "undefined"){
14812             c.dataIndex = i;
14813         }
14814         if(typeof c.renderer == "string"){
14815             c.renderer = Roo.util.Format[c.renderer];
14816         }
14817         if(typeof c.id == "undefined"){
14818             c.id = Roo.id();
14819         }
14820 //        if(c.editor && c.editor.xtype){
14821 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14822 //        }
14823 //        if(c.editor && c.editor.isFormField){
14824 //            c.editor = new Roo.grid.GridEditor(c.editor);
14825 //        }
14826
14827         this.lookup[c.id] = c;
14828     }
14829
14830     /**
14831      * The width of columns which have no width specified (defaults to 100)
14832      * @type Number
14833      */
14834     this.defaultWidth = 100;
14835
14836     /**
14837      * Default sortable of columns which have no sortable specified (defaults to false)
14838      * @type Boolean
14839      */
14840     this.defaultSortable = false;
14841
14842     this.addEvents({
14843         /**
14844              * @event widthchange
14845              * Fires when the width of a column changes.
14846              * @param {ColumnModel} this
14847              * @param {Number} columnIndex The column index
14848              * @param {Number} newWidth The new width
14849              */
14850             "widthchange": true,
14851         /**
14852              * @event headerchange
14853              * Fires when the text of a header changes.
14854              * @param {ColumnModel} this
14855              * @param {Number} columnIndex The column index
14856              * @param {Number} newText The new header text
14857              */
14858             "headerchange": true,
14859         /**
14860              * @event hiddenchange
14861              * Fires when a column is hidden or "unhidden".
14862              * @param {ColumnModel} this
14863              * @param {Number} columnIndex The column index
14864              * @param {Boolean} hidden true if hidden, false otherwise
14865              */
14866             "hiddenchange": true,
14867             /**
14868          * @event columnmoved
14869          * Fires when a column is moved.
14870          * @param {ColumnModel} this
14871          * @param {Number} oldIndex
14872          * @param {Number} newIndex
14873          */
14874         "columnmoved" : true,
14875         /**
14876          * @event columlockchange
14877          * Fires when a column's locked state is changed
14878          * @param {ColumnModel} this
14879          * @param {Number} colIndex
14880          * @param {Boolean} locked true if locked
14881          */
14882         "columnlockchange" : true
14883     });
14884     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14885 };
14886 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14887     /**
14888      * @cfg {String} header The header text to display in the Grid view.
14889      */
14890     /**
14891      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14892      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14893      * specified, the column's index is used as an index into the Record's data Array.
14894      */
14895     /**
14896      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14897      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14898      */
14899     /**
14900      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14901      * Defaults to the value of the {@link #defaultSortable} property.
14902      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14903      */
14904     /**
14905      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14906      */
14907     /**
14908      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14909      */
14910     /**
14911      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14912      */
14913     /**
14914      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14915      */
14916     /**
14917      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14918      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14919      * default renderer uses the raw data value.
14920      */
14921     /**
14922      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14923      */
14924
14925     /**
14926      * Returns the id of the column at the specified index.
14927      * @param {Number} index The column index
14928      * @return {String} the id
14929      */
14930     getColumnId : function(index){
14931         return this.config[index].id;
14932     },
14933
14934     /**
14935      * Returns the column for a specified id.
14936      * @param {String} id The column id
14937      * @return {Object} the column
14938      */
14939     getColumnById : function(id){
14940         return this.lookup[id];
14941     },
14942
14943     
14944     /**
14945      * Returns the column for a specified dataIndex.
14946      * @param {String} dataIndex The column dataIndex
14947      * @return {Object|Boolean} the column or false if not found
14948      */
14949     getColumnByDataIndex: function(dataIndex){
14950         var index = this.findColumnIndex(dataIndex);
14951         return index > -1 ? this.config[index] : false;
14952     },
14953     
14954     /**
14955      * Returns the index for a specified column id.
14956      * @param {String} id The column id
14957      * @return {Number} the index, or -1 if not found
14958      */
14959     getIndexById : function(id){
14960         for(var i = 0, len = this.config.length; i < len; i++){
14961             if(this.config[i].id == id){
14962                 return i;
14963             }
14964         }
14965         return -1;
14966     },
14967     
14968     /**
14969      * Returns the index for a specified column dataIndex.
14970      * @param {String} dataIndex The column dataIndex
14971      * @return {Number} the index, or -1 if not found
14972      */
14973     
14974     findColumnIndex : function(dataIndex){
14975         for(var i = 0, len = this.config.length; i < len; i++){
14976             if(this.config[i].dataIndex == dataIndex){
14977                 return i;
14978             }
14979         }
14980         return -1;
14981     },
14982     
14983     
14984     moveColumn : function(oldIndex, newIndex){
14985         var c = this.config[oldIndex];
14986         this.config.splice(oldIndex, 1);
14987         this.config.splice(newIndex, 0, c);
14988         this.dataMap = null;
14989         this.fireEvent("columnmoved", this, oldIndex, newIndex);
14990     },
14991
14992     isLocked : function(colIndex){
14993         return this.config[colIndex].locked === true;
14994     },
14995
14996     setLocked : function(colIndex, value, suppressEvent){
14997         if(this.isLocked(colIndex) == value){
14998             return;
14999         }
15000         this.config[colIndex].locked = value;
15001         if(!suppressEvent){
15002             this.fireEvent("columnlockchange", this, colIndex, value);
15003         }
15004     },
15005
15006     getTotalLockedWidth : function(){
15007         var totalWidth = 0;
15008         for(var i = 0; i < this.config.length; i++){
15009             if(this.isLocked(i) && !this.isHidden(i)){
15010                 this.totalWidth += this.getColumnWidth(i);
15011             }
15012         }
15013         return totalWidth;
15014     },
15015
15016     getLockedCount : function(){
15017         for(var i = 0, len = this.config.length; i < len; i++){
15018             if(!this.isLocked(i)){
15019                 return i;
15020             }
15021         }
15022     },
15023
15024     /**
15025      * Returns the number of columns.
15026      * @return {Number}
15027      */
15028     getColumnCount : function(visibleOnly){
15029         if(visibleOnly === true){
15030             var c = 0;
15031             for(var i = 0, len = this.config.length; i < len; i++){
15032                 if(!this.isHidden(i)){
15033                     c++;
15034                 }
15035             }
15036             return c;
15037         }
15038         return this.config.length;
15039     },
15040
15041     /**
15042      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15043      * @param {Function} fn
15044      * @param {Object} scope (optional)
15045      * @return {Array} result
15046      */
15047     getColumnsBy : function(fn, scope){
15048         var r = [];
15049         for(var i = 0, len = this.config.length; i < len; i++){
15050             var c = this.config[i];
15051             if(fn.call(scope||this, c, i) === true){
15052                 r[r.length] = c;
15053             }
15054         }
15055         return r;
15056     },
15057
15058     /**
15059      * Returns true if the specified column is sortable.
15060      * @param {Number} col The column index
15061      * @return {Boolean}
15062      */
15063     isSortable : function(col){
15064         if(typeof this.config[col].sortable == "undefined"){
15065             return this.defaultSortable;
15066         }
15067         return this.config[col].sortable;
15068     },
15069
15070     /**
15071      * Returns the rendering (formatting) function defined for the column.
15072      * @param {Number} col The column index.
15073      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15074      */
15075     getRenderer : function(col){
15076         if(!this.config[col].renderer){
15077             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15078         }
15079         return this.config[col].renderer;
15080     },
15081
15082     /**
15083      * Sets the rendering (formatting) function for a column.
15084      * @param {Number} col The column index
15085      * @param {Function} fn The function to use to process the cell's raw data
15086      * to return HTML markup for the grid view. The render function is called with
15087      * the following parameters:<ul>
15088      * <li>Data value.</li>
15089      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15090      * <li>css A CSS style string to apply to the table cell.</li>
15091      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15092      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15093      * <li>Row index</li>
15094      * <li>Column index</li>
15095      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15096      */
15097     setRenderer : function(col, fn){
15098         this.config[col].renderer = fn;
15099     },
15100
15101     /**
15102      * Returns the width for the specified column.
15103      * @param {Number} col The column index
15104      * @return {Number}
15105      */
15106     getColumnWidth : function(col){
15107         return this.config[col].width * 1 || this.defaultWidth;
15108     },
15109
15110     /**
15111      * Sets the width for a column.
15112      * @param {Number} col The column index
15113      * @param {Number} width The new width
15114      */
15115     setColumnWidth : function(col, width, suppressEvent){
15116         this.config[col].width = width;
15117         this.totalWidth = null;
15118         if(!suppressEvent){
15119              this.fireEvent("widthchange", this, col, width);
15120         }
15121     },
15122
15123     /**
15124      * Returns the total width of all columns.
15125      * @param {Boolean} includeHidden True to include hidden column widths
15126      * @return {Number}
15127      */
15128     getTotalWidth : function(includeHidden){
15129         if(!this.totalWidth){
15130             this.totalWidth = 0;
15131             for(var i = 0, len = this.config.length; i < len; i++){
15132                 if(includeHidden || !this.isHidden(i)){
15133                     this.totalWidth += this.getColumnWidth(i);
15134                 }
15135             }
15136         }
15137         return this.totalWidth;
15138     },
15139
15140     /**
15141      * Returns the header for the specified column.
15142      * @param {Number} col The column index
15143      * @return {String}
15144      */
15145     getColumnHeader : function(col){
15146         return this.config[col].header;
15147     },
15148
15149     /**
15150      * Sets the header for a column.
15151      * @param {Number} col The column index
15152      * @param {String} header The new header
15153      */
15154     setColumnHeader : function(col, header){
15155         this.config[col].header = header;
15156         this.fireEvent("headerchange", this, col, header);
15157     },
15158
15159     /**
15160      * Returns the tooltip for the specified column.
15161      * @param {Number} col The column index
15162      * @return {String}
15163      */
15164     getColumnTooltip : function(col){
15165             return this.config[col].tooltip;
15166     },
15167     /**
15168      * Sets the tooltip for a column.
15169      * @param {Number} col The column index
15170      * @param {String} tooltip The new tooltip
15171      */
15172     setColumnTooltip : function(col, tooltip){
15173             this.config[col].tooltip = tooltip;
15174     },
15175
15176     /**
15177      * Returns the dataIndex for the specified column.
15178      * @param {Number} col The column index
15179      * @return {Number}
15180      */
15181     getDataIndex : function(col){
15182         return this.config[col].dataIndex;
15183     },
15184
15185     /**
15186      * Sets the dataIndex for a column.
15187      * @param {Number} col The column index
15188      * @param {Number} dataIndex The new dataIndex
15189      */
15190     setDataIndex : function(col, dataIndex){
15191         this.config[col].dataIndex = dataIndex;
15192     },
15193
15194     
15195     
15196     /**
15197      * Returns true if the cell is editable.
15198      * @param {Number} colIndex The column index
15199      * @param {Number} rowIndex The row index
15200      * @return {Boolean}
15201      */
15202     isCellEditable : function(colIndex, rowIndex){
15203         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15204     },
15205
15206     /**
15207      * Returns the editor defined for the cell/column.
15208      * return false or null to disable editing.
15209      * @param {Number} colIndex The column index
15210      * @param {Number} rowIndex The row index
15211      * @return {Object}
15212      */
15213     getCellEditor : function(colIndex, rowIndex){
15214         return this.config[colIndex].editor;
15215     },
15216
15217     /**
15218      * Sets if a column is editable.
15219      * @param {Number} col The column index
15220      * @param {Boolean} editable True if the column is editable
15221      */
15222     setEditable : function(col, editable){
15223         this.config[col].editable = editable;
15224     },
15225
15226
15227     /**
15228      * Returns true if the column is hidden.
15229      * @param {Number} colIndex The column index
15230      * @return {Boolean}
15231      */
15232     isHidden : function(colIndex){
15233         return this.config[colIndex].hidden;
15234     },
15235
15236
15237     /**
15238      * Returns true if the column width cannot be changed
15239      */
15240     isFixed : function(colIndex){
15241         return this.config[colIndex].fixed;
15242     },
15243
15244     /**
15245      * Returns true if the column can be resized
15246      * @return {Boolean}
15247      */
15248     isResizable : function(colIndex){
15249         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15250     },
15251     /**
15252      * Sets if a column is hidden.
15253      * @param {Number} colIndex The column index
15254      * @param {Boolean} hidden True if the column is hidden
15255      */
15256     setHidden : function(colIndex, hidden){
15257         this.config[colIndex].hidden = hidden;
15258         this.totalWidth = null;
15259         this.fireEvent("hiddenchange", this, colIndex, hidden);
15260     },
15261
15262     /**
15263      * Sets the editor for a column.
15264      * @param {Number} col The column index
15265      * @param {Object} editor The editor object
15266      */
15267     setEditor : function(col, editor){
15268         this.config[col].editor = editor;
15269     }
15270 });
15271
15272 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15273         if(typeof value == "string" && value.length < 1){
15274             return "&#160;";
15275         }
15276         return value;
15277 };
15278
15279 // Alias for backwards compatibility
15280 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15281
15282 /**
15283  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15284  * @class Roo.bootstrap.Table.RowSelectionModel
15285  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15286  * It supports multiple selections and keyboard selection/navigation. 
15287  * @constructor
15288  * @param {Object} config
15289  */
15290
15291 Roo.bootstrap.Table.RowSelectionModel = function(config){
15292     Roo.apply(this, config);
15293     this.selections = new Roo.util.MixedCollection(false, function(o){
15294         return o.id;
15295     });
15296
15297     this.last = false;
15298     this.lastActive = false;
15299
15300     this.addEvents({
15301         /**
15302              * @event selectionchange
15303              * Fires when the selection changes
15304              * @param {SelectionModel} this
15305              */
15306             "selectionchange" : true,
15307         /**
15308              * @event afterselectionchange
15309              * Fires after the selection changes (eg. by key press or clicking)
15310              * @param {SelectionModel} this
15311              */
15312             "afterselectionchange" : true,
15313         /**
15314              * @event beforerowselect
15315              * Fires when a row is selected being selected, return false to cancel.
15316              * @param {SelectionModel} this
15317              * @param {Number} rowIndex The selected index
15318              * @param {Boolean} keepExisting False if other selections will be cleared
15319              */
15320             "beforerowselect" : true,
15321         /**
15322              * @event rowselect
15323              * Fires when a row is selected.
15324              * @param {SelectionModel} this
15325              * @param {Number} rowIndex The selected index
15326              * @param {Roo.data.Record} r The record
15327              */
15328             "rowselect" : true,
15329         /**
15330              * @event rowdeselect
15331              * Fires when a row is deselected.
15332              * @param {SelectionModel} this
15333              * @param {Number} rowIndex The selected index
15334              */
15335         "rowdeselect" : true
15336     });
15337     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15338     this.locked = false;
15339 };
15340
15341 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15342     /**
15343      * @cfg {Boolean} singleSelect
15344      * True to allow selection of only one row at a time (defaults to false)
15345      */
15346     singleSelect : false,
15347
15348     // private
15349     initEvents : function(){
15350
15351         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15352             this.grid.on("mousedown", this.handleMouseDown, this);
15353         }else{ // allow click to work like normal
15354             this.grid.on("rowclick", this.handleDragableRowClick, this);
15355         }
15356
15357         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15358             "up" : function(e){
15359                 if(!e.shiftKey){
15360                     this.selectPrevious(e.shiftKey);
15361                 }else if(this.last !== false && this.lastActive !== false){
15362                     var last = this.last;
15363                     this.selectRange(this.last,  this.lastActive-1);
15364                     this.grid.getView().focusRow(this.lastActive);
15365                     if(last !== false){
15366                         this.last = last;
15367                     }
15368                 }else{
15369                     this.selectFirstRow();
15370                 }
15371                 this.fireEvent("afterselectionchange", this);
15372             },
15373             "down" : function(e){
15374                 if(!e.shiftKey){
15375                     this.selectNext(e.shiftKey);
15376                 }else if(this.last !== false && this.lastActive !== false){
15377                     var last = this.last;
15378                     this.selectRange(this.last,  this.lastActive+1);
15379                     this.grid.getView().focusRow(this.lastActive);
15380                     if(last !== false){
15381                         this.last = last;
15382                     }
15383                 }else{
15384                     this.selectFirstRow();
15385                 }
15386                 this.fireEvent("afterselectionchange", this);
15387             },
15388             scope: this
15389         });
15390
15391         var view = this.grid.view;
15392         view.on("refresh", this.onRefresh, this);
15393         view.on("rowupdated", this.onRowUpdated, this);
15394         view.on("rowremoved", this.onRemove, this);
15395     },
15396
15397     // private
15398     onRefresh : function(){
15399         var ds = this.grid.dataSource, i, v = this.grid.view;
15400         var s = this.selections;
15401         s.each(function(r){
15402             if((i = ds.indexOfId(r.id)) != -1){
15403                 v.onRowSelect(i);
15404             }else{
15405                 s.remove(r);
15406             }
15407         });
15408     },
15409
15410     // private
15411     onRemove : function(v, index, r){
15412         this.selections.remove(r);
15413     },
15414
15415     // private
15416     onRowUpdated : function(v, index, r){
15417         if(this.isSelected(r)){
15418             v.onRowSelect(index);
15419         }
15420     },
15421
15422     /**
15423      * Select records.
15424      * @param {Array} records The records to select
15425      * @param {Boolean} keepExisting (optional) True to keep existing selections
15426      */
15427     selectRecords : function(records, keepExisting){
15428         if(!keepExisting){
15429             this.clearSelections();
15430         }
15431         var ds = this.grid.dataSource;
15432         for(var i = 0, len = records.length; i < len; i++){
15433             this.selectRow(ds.indexOf(records[i]), true);
15434         }
15435     },
15436
15437     /**
15438      * Gets the number of selected rows.
15439      * @return {Number}
15440      */
15441     getCount : function(){
15442         return this.selections.length;
15443     },
15444
15445     /**
15446      * Selects the first row in the grid.
15447      */
15448     selectFirstRow : function(){
15449         this.selectRow(0);
15450     },
15451
15452     /**
15453      * Select the last row.
15454      * @param {Boolean} keepExisting (optional) True to keep existing selections
15455      */
15456     selectLastRow : function(keepExisting){
15457         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15458     },
15459
15460     /**
15461      * Selects the row immediately following the last selected row.
15462      * @param {Boolean} keepExisting (optional) True to keep existing selections
15463      */
15464     selectNext : function(keepExisting){
15465         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15466             this.selectRow(this.last+1, keepExisting);
15467             this.grid.getView().focusRow(this.last);
15468         }
15469     },
15470
15471     /**
15472      * Selects the row that precedes the last selected row.
15473      * @param {Boolean} keepExisting (optional) True to keep existing selections
15474      */
15475     selectPrevious : function(keepExisting){
15476         if(this.last){
15477             this.selectRow(this.last-1, keepExisting);
15478             this.grid.getView().focusRow(this.last);
15479         }
15480     },
15481
15482     /**
15483      * Returns the selected records
15484      * @return {Array} Array of selected records
15485      */
15486     getSelections : function(){
15487         return [].concat(this.selections.items);
15488     },
15489
15490     /**
15491      * Returns the first selected record.
15492      * @return {Record}
15493      */
15494     getSelected : function(){
15495         return this.selections.itemAt(0);
15496     },
15497
15498
15499     /**
15500      * Clears all selections.
15501      */
15502     clearSelections : function(fast){
15503         if(this.locked) return;
15504         if(fast !== true){
15505             var ds = this.grid.dataSource;
15506             var s = this.selections;
15507             s.each(function(r){
15508                 this.deselectRow(ds.indexOfId(r.id));
15509             }, this);
15510             s.clear();
15511         }else{
15512             this.selections.clear();
15513         }
15514         this.last = false;
15515     },
15516
15517
15518     /**
15519      * Selects all rows.
15520      */
15521     selectAll : function(){
15522         if(this.locked) return;
15523         this.selections.clear();
15524         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15525             this.selectRow(i, true);
15526         }
15527     },
15528
15529     /**
15530      * Returns True if there is a selection.
15531      * @return {Boolean}
15532      */
15533     hasSelection : function(){
15534         return this.selections.length > 0;
15535     },
15536
15537     /**
15538      * Returns True if the specified row is selected.
15539      * @param {Number/Record} record The record or index of the record to check
15540      * @return {Boolean}
15541      */
15542     isSelected : function(index){
15543         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15544         return (r && this.selections.key(r.id) ? true : false);
15545     },
15546
15547     /**
15548      * Returns True if the specified record id is selected.
15549      * @param {String} id The id of record to check
15550      * @return {Boolean}
15551      */
15552     isIdSelected : function(id){
15553         return (this.selections.key(id) ? true : false);
15554     },
15555
15556     // private
15557     handleMouseDown : function(e, t){
15558         var view = this.grid.getView(), rowIndex;
15559         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15560             return;
15561         };
15562         if(e.shiftKey && this.last !== false){
15563             var last = this.last;
15564             this.selectRange(last, rowIndex, e.ctrlKey);
15565             this.last = last; // reset the last
15566             view.focusRow(rowIndex);
15567         }else{
15568             var isSelected = this.isSelected(rowIndex);
15569             if(e.button !== 0 && isSelected){
15570                 view.focusRow(rowIndex);
15571             }else if(e.ctrlKey && isSelected){
15572                 this.deselectRow(rowIndex);
15573             }else if(!isSelected){
15574                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15575                 view.focusRow(rowIndex);
15576             }
15577         }
15578         this.fireEvent("afterselectionchange", this);
15579     },
15580     // private
15581     handleDragableRowClick :  function(grid, rowIndex, e) 
15582     {
15583         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15584             this.selectRow(rowIndex, false);
15585             grid.view.focusRow(rowIndex);
15586              this.fireEvent("afterselectionchange", this);
15587         }
15588     },
15589     
15590     /**
15591      * Selects multiple rows.
15592      * @param {Array} rows Array of the indexes of the row to select
15593      * @param {Boolean} keepExisting (optional) True to keep existing selections
15594      */
15595     selectRows : function(rows, keepExisting){
15596         if(!keepExisting){
15597             this.clearSelections();
15598         }
15599         for(var i = 0, len = rows.length; i < len; i++){
15600             this.selectRow(rows[i], true);
15601         }
15602     },
15603
15604     /**
15605      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15606      * @param {Number} startRow The index of the first row in the range
15607      * @param {Number} endRow The index of the last row in the range
15608      * @param {Boolean} keepExisting (optional) True to retain existing selections
15609      */
15610     selectRange : function(startRow, endRow, keepExisting){
15611         if(this.locked) return;
15612         if(!keepExisting){
15613             this.clearSelections();
15614         }
15615         if(startRow <= endRow){
15616             for(var i = startRow; i <= endRow; i++){
15617                 this.selectRow(i, true);
15618             }
15619         }else{
15620             for(var i = startRow; i >= endRow; i--){
15621                 this.selectRow(i, true);
15622             }
15623         }
15624     },
15625
15626     /**
15627      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15628      * @param {Number} startRow The index of the first row in the range
15629      * @param {Number} endRow The index of the last row in the range
15630      */
15631     deselectRange : function(startRow, endRow, preventViewNotify){
15632         if(this.locked) return;
15633         for(var i = startRow; i <= endRow; i++){
15634             this.deselectRow(i, preventViewNotify);
15635         }
15636     },
15637
15638     /**
15639      * Selects a row.
15640      * @param {Number} row The index of the row to select
15641      * @param {Boolean} keepExisting (optional) True to keep existing selections
15642      */
15643     selectRow : function(index, keepExisting, preventViewNotify){
15644         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15645         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15646             if(!keepExisting || this.singleSelect){
15647                 this.clearSelections();
15648             }
15649             var r = this.grid.dataSource.getAt(index);
15650             this.selections.add(r);
15651             this.last = this.lastActive = index;
15652             if(!preventViewNotify){
15653                 this.grid.getView().onRowSelect(index);
15654             }
15655             this.fireEvent("rowselect", this, index, r);
15656             this.fireEvent("selectionchange", this);
15657         }
15658     },
15659
15660     /**
15661      * Deselects a row.
15662      * @param {Number} row The index of the row to deselect
15663      */
15664     deselectRow : function(index, preventViewNotify){
15665         if(this.locked) return;
15666         if(this.last == index){
15667             this.last = false;
15668         }
15669         if(this.lastActive == index){
15670             this.lastActive = false;
15671         }
15672         var r = this.grid.dataSource.getAt(index);
15673         this.selections.remove(r);
15674         if(!preventViewNotify){
15675             this.grid.getView().onRowDeselect(index);
15676         }
15677         this.fireEvent("rowdeselect", this, index);
15678         this.fireEvent("selectionchange", this);
15679     },
15680
15681     // private
15682     restoreLast : function(){
15683         if(this._last){
15684             this.last = this._last;
15685         }
15686     },
15687
15688     // private
15689     acceptsNav : function(row, col, cm){
15690         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15691     },
15692
15693     // private
15694     onEditorKey : function(field, e){
15695         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15696         if(k == e.TAB){
15697             e.stopEvent();
15698             ed.completeEdit();
15699             if(e.shiftKey){
15700                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15701             }else{
15702                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15703             }
15704         }else if(k == e.ENTER && !e.ctrlKey){
15705             e.stopEvent();
15706             ed.completeEdit();
15707             if(e.shiftKey){
15708                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15709             }else{
15710                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15711             }
15712         }else if(k == e.ESC){
15713             ed.cancelEdit();
15714         }
15715         if(newCell){
15716             g.startEditing(newCell[0], newCell[1]);
15717         }
15718     }
15719 });/*
15720  * - LGPL
15721  *
15722  * element
15723  * 
15724  */
15725
15726 /**
15727  * @class Roo.bootstrap.MessageBar
15728  * @extends Roo.bootstrap.Component
15729  * Bootstrap MessageBar class
15730  * @cfg {String} html contents of the MessageBar
15731  * @cfg {String} weight (info | success | warning | danger) default info
15732  * @cfg {String} beforeClass insert the bar before the given class
15733  * @cfg {Boolean} closable (true | false) default false
15734  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15735  * 
15736  * @constructor
15737  * Create a new Element
15738  * @param {Object} config The config object
15739  */
15740
15741 Roo.bootstrap.MessageBar = function(config){
15742     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15743 };
15744
15745 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15746     
15747     html: '',
15748     weight: 'info',
15749     closable: false,
15750     fixed: false,
15751     beforeClass: 'bootstrap-sticky-wrap',
15752     
15753     getAutoCreate : function(){
15754         
15755         var cfg = {
15756             tag: 'div',
15757             cls: 'alert alert-dismissable alert-' + this.weight,
15758             cn: [
15759                 {
15760                     tag: 'span',
15761                     cls: 'message',
15762                     html: this.html || ''
15763                 }
15764             ]
15765         }
15766         
15767         if(this.fixed){
15768             cfg.cls += ' alert-messages-fixed';
15769         }
15770         
15771         if(this.closable){
15772             cfg.cn.push({
15773                 tag: 'button',
15774                 cls: 'close',
15775                 html: 'x'
15776             });
15777         }
15778         
15779         return cfg;
15780     },
15781     
15782     onRender : function(ct, position)
15783     {
15784         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15785         
15786         if(!this.el){
15787             var cfg = Roo.apply({},  this.getAutoCreate());
15788             cfg.id = Roo.id();
15789             
15790             if (this.cls) {
15791                 cfg.cls += ' ' + this.cls;
15792             }
15793             if (this.style) {
15794                 cfg.style = this.style;
15795             }
15796             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15797             
15798             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15799         }
15800         
15801         this.el.select('>button.close').on('click', this.hide, this);
15802         
15803     },
15804     
15805     show : function()
15806     {
15807         if (!this.rendered) {
15808             this.render();
15809         }
15810         
15811         this.el.show();
15812         
15813         this.fireEvent('show', this);
15814         
15815     },
15816     
15817     hide : function()
15818     {
15819         if (!this.rendered) {
15820             this.render();
15821         }
15822         
15823         this.el.hide();
15824         
15825         this.fireEvent('hide', this);
15826     },
15827     
15828     update : function()
15829     {
15830 //        var e = this.el.dom.firstChild;
15831 //        
15832 //        if(this.closable){
15833 //            e = e.nextSibling;
15834 //        }
15835 //        
15836 //        e.data = this.html || '';
15837
15838         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15839     }
15840    
15841 });
15842
15843  
15844
15845