roojs-bootstrap.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(this.boxLable);
12578         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12579     }
12580     
12581 });
12582
12583  
12584 /*
12585  * - LGPL
12586  *
12587  * Radio
12588  * 
12589  */
12590
12591 /**
12592  * @class Roo.bootstrap.Radio
12593  * @extends Roo.bootstrap.CheckBox
12594  * Bootstrap Radio class
12595
12596  * @constructor
12597  * Create a new Radio
12598  * @param {Object} config The config object
12599  */
12600
12601 Roo.bootstrap.Radio = function(config){
12602     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12603    
12604 };
12605
12606 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12607     
12608     inputType: 'radio',
12609     inputValue: '',
12610     valueOff: '',
12611     
12612     getAutoCreate : function()
12613     {
12614         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12615         
12616         var id = Roo.id();
12617         
12618         var cfg = {};
12619         
12620         cfg.cls = 'form-group' //input-group
12621         
12622         var input =  {
12623             tag: 'input',
12624             id : id,
12625             type : this.inputType,
12626             value : (!this.checked) ? this.valueOff : this.inputValue,
12627             cls : 'form-box',
12628             placeholder : this.placeholder || ''
12629             
12630         };
12631         
12632         if (this.disabled) {
12633             input.disabled=true;
12634         }
12635         
12636         if(this.checked){
12637             input.checked = this.checked;
12638         }
12639         
12640         if (this.name) {
12641             input.name = this.name;
12642         }
12643         
12644         if (this.size) {
12645             input.cls += ' input-' + this.size;
12646         }
12647         
12648         var settings=this;
12649         ['xs','sm','md','lg'].map(function(size){
12650             if (settings[size]) {
12651                 cfg.cls += ' col-' + size + '-' + settings[size];
12652             }
12653         });
12654         
12655         var inputblock = input;
12656         
12657         if (this.before || this.after) {
12658             
12659             inputblock = {
12660                 cls : 'input-group',
12661                 cn :  [] 
12662             };
12663             if (this.before) {
12664                 inputblock.cn.push({
12665                     tag :'span',
12666                     cls : 'input-group-addon',
12667                     html : this.before
12668                 });
12669             }
12670             inputblock.cn.push(input);
12671             if (this.after) {
12672                 inputblock.cn.push({
12673                     tag :'span',
12674                     cls : 'input-group-addon',
12675                     html : this.after
12676                 });
12677             }
12678             
12679         };
12680         
12681         if (align ==='left' && this.fieldLabel.length) {
12682                 Roo.log("left and has label");
12683                 cfg.cn = [
12684                     
12685                     {
12686                         tag: 'label',
12687                         'for' :  id,
12688                         cls : 'control-label col-md-' + this.labelWidth,
12689                         html : this.fieldLabel
12690                         
12691                     },
12692                     {
12693                         cls : "col-md-" + (12 - this.labelWidth), 
12694                         cn: [
12695                             inputblock
12696                         ]
12697                     }
12698                     
12699                 ];
12700         } else if ( this.fieldLabel.length) {
12701                 Roo.log(" label");
12702                  cfg.cn = [
12703                    
12704                     {
12705                         tag: 'label',
12706                         'for': id,
12707                         cls: 'control-label box-input-label',
12708                         //cls : 'input-group-addon',
12709                         html : this.fieldLabel
12710                         
12711                     },
12712                     
12713                     inputblock
12714                     
12715                 ];
12716
12717         } else {
12718             
12719                    Roo.log(" no label && no align");
12720                 cfg.cn = [
12721                     
12722                         inputblock
12723                     
12724                 ];
12725                 
12726                 
12727         };
12728         
12729         if(this.boxLabel){
12730             cfg.cn.push({
12731                 tag: 'label',
12732                 'for': id,
12733                 cls: 'box-label',
12734                 html: this.boxLabel
12735             })
12736         }
12737         
12738         return cfg;
12739         
12740     },
12741    
12742     onClick : function()
12743     {   
12744         this.setChecked(true);
12745     },
12746     
12747     setChecked : function(state,suppressEvent)
12748     {
12749         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12750             v.dom.checked = false;
12751         });
12752         
12753         this.checked = state;
12754         this.inputEl().dom.checked = state;
12755         
12756         if(suppressEvent !== true){
12757             this.fireEvent('check', this, state);
12758         }
12759         
12760         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12761         
12762     },
12763     
12764     getGroupValue : function()
12765     {
12766         var value = ''
12767         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12768             if(v.dom.checked == true){
12769                 value = v.dom.value;
12770             }
12771         });
12772         
12773         return value;
12774     },
12775     
12776     /**
12777      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12778      * @return {Mixed} value The field value
12779      */
12780     getValue : function(){
12781         return this.getGroupValue();
12782     }
12783     
12784 });
12785
12786  
12787 //<script type="text/javascript">
12788
12789 /*
12790  * Based  Ext JS Library 1.1.1
12791  * Copyright(c) 2006-2007, Ext JS, LLC.
12792  * LGPL
12793  *
12794  */
12795  
12796 /**
12797  * @class Roo.HtmlEditorCore
12798  * @extends Roo.Component
12799  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12800  *
12801  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12802  */
12803
12804 Roo.HtmlEditorCore = function(config){
12805     
12806     
12807     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12808     this.addEvents({
12809         /**
12810          * @event initialize
12811          * Fires when the editor is fully initialized (including the iframe)
12812          * @param {Roo.HtmlEditorCore} this
12813          */
12814         initialize: true,
12815         /**
12816          * @event activate
12817          * Fires when the editor is first receives the focus. Any insertion must wait
12818          * until after this event.
12819          * @param {Roo.HtmlEditorCore} this
12820          */
12821         activate: true,
12822          /**
12823          * @event beforesync
12824          * Fires before the textarea is updated with content from the editor iframe. Return false
12825          * to cancel the sync.
12826          * @param {Roo.HtmlEditorCore} this
12827          * @param {String} html
12828          */
12829         beforesync: true,
12830          /**
12831          * @event beforepush
12832          * Fires before the iframe editor is updated with content from the textarea. Return false
12833          * to cancel the push.
12834          * @param {Roo.HtmlEditorCore} this
12835          * @param {String} html
12836          */
12837         beforepush: true,
12838          /**
12839          * @event sync
12840          * Fires when the textarea is updated with content from the editor iframe.
12841          * @param {Roo.HtmlEditorCore} this
12842          * @param {String} html
12843          */
12844         sync: true,
12845          /**
12846          * @event push
12847          * Fires when the iframe editor is updated with content from the textarea.
12848          * @param {Roo.HtmlEditorCore} this
12849          * @param {String} html
12850          */
12851         push: true,
12852         
12853         /**
12854          * @event editorevent
12855          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12856          * @param {Roo.HtmlEditorCore} this
12857          */
12858         editorevent: true
12859     });
12860      
12861 };
12862
12863
12864 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12865
12866
12867      /**
12868      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12869      */
12870     
12871     owner : false,
12872     
12873      /**
12874      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12875      *                        Roo.resizable.
12876      */
12877     resizable : false,
12878      /**
12879      * @cfg {Number} height (in pixels)
12880      */   
12881     height: 300,
12882    /**
12883      * @cfg {Number} width (in pixels)
12884      */   
12885     width: 500,
12886     
12887     /**
12888      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12889      * 
12890      */
12891     stylesheets: false,
12892     
12893     // id of frame..
12894     frameId: false,
12895     
12896     // private properties
12897     validationEvent : false,
12898     deferHeight: true,
12899     initialized : false,
12900     activated : false,
12901     sourceEditMode : false,
12902     onFocus : Roo.emptyFn,
12903     iframePad:3,
12904     hideMode:'offsets',
12905     
12906      
12907     
12908
12909     /**
12910      * Protected method that will not generally be called directly. It
12911      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12912      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12913      */
12914     getDocMarkup : function(){
12915         // body styles..
12916         var st = '';
12917         Roo.log(this.stylesheets);
12918         
12919         // inherit styels from page...?? 
12920         if (this.stylesheets === false) {
12921             
12922             Roo.get(document.head).select('style').each(function(node) {
12923                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12924             });
12925             
12926             Roo.get(document.head).select('link').each(function(node) { 
12927                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12928             });
12929             
12930         } else if (!this.stylesheets.length) {
12931                 // simple..
12932                 st = '<style type="text/css">' +
12933                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12934                    '</style>';
12935         } else {
12936             Roo.each(this.stylesheets, function(s) {
12937                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12938             });
12939             
12940         }
12941         
12942         st +=  '<style type="text/css">' +
12943             'IMG { cursor: pointer } ' +
12944         '</style>';
12945
12946         
12947         return '<html><head>' + st  +
12948             //<style type="text/css">' +
12949             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12950             //'</style>' +
12951             ' </head><body class="roo-htmleditor-body"></body></html>';
12952     },
12953
12954     // private
12955     onRender : function(ct, position)
12956     {
12957         var _t = this;
12958         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12959         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12960         
12961         
12962         this.el.dom.style.border = '0 none';
12963         this.el.dom.setAttribute('tabIndex', -1);
12964         this.el.addClass('x-hidden hide');
12965         
12966         
12967         
12968         if(Roo.isIE){ // fix IE 1px bogus margin
12969             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12970         }
12971        
12972         
12973         this.frameId = Roo.id();
12974         
12975          
12976         
12977         var iframe = this.owner.wrap.createChild({
12978             tag: 'iframe',
12979             cls: 'form-control', // bootstrap..
12980             id: this.frameId,
12981             name: this.frameId,
12982             frameBorder : 'no',
12983             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
12984         }, this.el
12985         );
12986         
12987         
12988         this.iframe = iframe.dom;
12989
12990          this.assignDocWin();
12991         
12992         this.doc.designMode = 'on';
12993        
12994         this.doc.open();
12995         this.doc.write(this.getDocMarkup());
12996         this.doc.close();
12997
12998         
12999         var task = { // must defer to wait for browser to be ready
13000             run : function(){
13001                 //console.log("run task?" + this.doc.readyState);
13002                 this.assignDocWin();
13003                 if(this.doc.body || this.doc.readyState == 'complete'){
13004                     try {
13005                         this.doc.designMode="on";
13006                     } catch (e) {
13007                         return;
13008                     }
13009                     Roo.TaskMgr.stop(task);
13010                     this.initEditor.defer(10, this);
13011                 }
13012             },
13013             interval : 10,
13014             duration: 10000,
13015             scope: this
13016         };
13017         Roo.TaskMgr.start(task);
13018
13019         
13020          
13021     },
13022
13023     // private
13024     onResize : function(w, h)
13025     {
13026          Roo.log('resize: ' +w + ',' + h );
13027         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13028         if(!this.iframe){
13029             return;
13030         }
13031         if(typeof w == 'number'){
13032             
13033             this.iframe.style.width = w + 'px';
13034         }
13035         if(typeof h == 'number'){
13036             
13037             this.iframe.style.height = h + 'px';
13038             if(this.doc){
13039                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13040             }
13041         }
13042         
13043     },
13044
13045     /**
13046      * Toggles the editor between standard and source edit mode.
13047      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13048      */
13049     toggleSourceEdit : function(sourceEditMode){
13050         
13051         this.sourceEditMode = sourceEditMode === true;
13052         
13053         if(this.sourceEditMode){
13054  
13055             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13056             
13057         }else{
13058             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13059             //this.iframe.className = '';
13060             this.deferFocus();
13061         }
13062         //this.setSize(this.owner.wrap.getSize());
13063         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13064     },
13065
13066     
13067   
13068
13069     /**
13070      * Protected method that will not generally be called directly. If you need/want
13071      * custom HTML cleanup, this is the method you should override.
13072      * @param {String} html The HTML to be cleaned
13073      * return {String} The cleaned HTML
13074      */
13075     cleanHtml : function(html){
13076         html = String(html);
13077         if(html.length > 5){
13078             if(Roo.isSafari){ // strip safari nonsense
13079                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13080             }
13081         }
13082         if(html == '&nbsp;'){
13083             html = '';
13084         }
13085         return html;
13086     },
13087
13088     /**
13089      * HTML Editor -> Textarea
13090      * Protected method that will not generally be called directly. Syncs the contents
13091      * of the editor iframe with the textarea.
13092      */
13093     syncValue : function(){
13094         if(this.initialized){
13095             var bd = (this.doc.body || this.doc.documentElement);
13096             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13097             var html = bd.innerHTML;
13098             if(Roo.isSafari){
13099                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13100                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13101                 if(m && m[1]){
13102                     html = '<div style="'+m[0]+'">' + html + '</div>';
13103                 }
13104             }
13105             html = this.cleanHtml(html);
13106             // fix up the special chars.. normaly like back quotes in word...
13107             // however we do not want to do this with chinese..
13108             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13109                 var cc = b.charCodeAt();
13110                 if (
13111                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13112                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13113                     (cc >= 0xf900 && cc < 0xfb00 )
13114                 ) {
13115                         return b;
13116                 }
13117                 return "&#"+cc+";" 
13118             });
13119             if(this.owner.fireEvent('beforesync', this, html) !== false){
13120                 this.el.dom.value = html;
13121                 this.owner.fireEvent('sync', this, html);
13122             }
13123         }
13124     },
13125
13126     /**
13127      * Protected method that will not generally be called directly. Pushes the value of the textarea
13128      * into the iframe editor.
13129      */
13130     pushValue : function(){
13131         if(this.initialized){
13132             var v = this.el.dom.value.trim();
13133             
13134 //            if(v.length < 1){
13135 //                v = '&#160;';
13136 //            }
13137             
13138             if(this.owner.fireEvent('beforepush', this, v) !== false){
13139                 var d = (this.doc.body || this.doc.documentElement);
13140                 d.innerHTML = v;
13141                 this.cleanUpPaste();
13142                 this.el.dom.value = d.innerHTML;
13143                 this.owner.fireEvent('push', this, v);
13144             }
13145         }
13146     },
13147
13148     // private
13149     deferFocus : function(){
13150         this.focus.defer(10, this);
13151     },
13152
13153     // doc'ed in Field
13154     focus : function(){
13155         if(this.win && !this.sourceEditMode){
13156             this.win.focus();
13157         }else{
13158             this.el.focus();
13159         }
13160     },
13161     
13162     assignDocWin: function()
13163     {
13164         var iframe = this.iframe;
13165         
13166          if(Roo.isIE){
13167             this.doc = iframe.contentWindow.document;
13168             this.win = iframe.contentWindow;
13169         } else {
13170             if (!Roo.get(this.frameId)) {
13171                 return;
13172             }
13173             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13174             this.win = Roo.get(this.frameId).dom.contentWindow;
13175         }
13176     },
13177     
13178     // private
13179     initEditor : function(){
13180         //console.log("INIT EDITOR");
13181         this.assignDocWin();
13182         
13183         
13184         
13185         this.doc.designMode="on";
13186         this.doc.open();
13187         this.doc.write(this.getDocMarkup());
13188         this.doc.close();
13189         
13190         var dbody = (this.doc.body || this.doc.documentElement);
13191         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13192         // this copies styles from the containing element into thsi one..
13193         // not sure why we need all of this..
13194         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13195         ss['background-attachment'] = 'fixed'; // w3c
13196         dbody.bgProperties = 'fixed'; // ie
13197         Roo.DomHelper.applyStyles(dbody, ss);
13198         Roo.EventManager.on(this.doc, {
13199             //'mousedown': this.onEditorEvent,
13200             'mouseup': this.onEditorEvent,
13201             'dblclick': this.onEditorEvent,
13202             'click': this.onEditorEvent,
13203             'keyup': this.onEditorEvent,
13204             buffer:100,
13205             scope: this
13206         });
13207         if(Roo.isGecko){
13208             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13209         }
13210         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13211             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13212         }
13213         this.initialized = true;
13214
13215         this.owner.fireEvent('initialize', this);
13216         this.pushValue();
13217     },
13218
13219     // private
13220     onDestroy : function(){
13221         
13222         
13223         
13224         if(this.rendered){
13225             
13226             //for (var i =0; i < this.toolbars.length;i++) {
13227             //    // fixme - ask toolbars for heights?
13228             //    this.toolbars[i].onDestroy();
13229            // }
13230             
13231             //this.wrap.dom.innerHTML = '';
13232             //this.wrap.remove();
13233         }
13234     },
13235
13236     // private
13237     onFirstFocus : function(){
13238         
13239         this.assignDocWin();
13240         
13241         
13242         this.activated = true;
13243          
13244     
13245         if(Roo.isGecko){ // prevent silly gecko errors
13246             this.win.focus();
13247             var s = this.win.getSelection();
13248             if(!s.focusNode || s.focusNode.nodeType != 3){
13249                 var r = s.getRangeAt(0);
13250                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13251                 r.collapse(true);
13252                 this.deferFocus();
13253             }
13254             try{
13255                 this.execCmd('useCSS', true);
13256                 this.execCmd('styleWithCSS', false);
13257             }catch(e){}
13258         }
13259         this.owner.fireEvent('activate', this);
13260     },
13261
13262     // private
13263     adjustFont: function(btn){
13264         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13265         //if(Roo.isSafari){ // safari
13266         //    adjust *= 2;
13267        // }
13268         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13269         if(Roo.isSafari){ // safari
13270             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13271             v =  (v < 10) ? 10 : v;
13272             v =  (v > 48) ? 48 : v;
13273             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13274             
13275         }
13276         
13277         
13278         v = Math.max(1, v+adjust);
13279         
13280         this.execCmd('FontSize', v  );
13281     },
13282
13283     onEditorEvent : function(e){
13284         this.owner.fireEvent('editorevent', this, e);
13285       //  this.updateToolbar();
13286         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13287     },
13288
13289     insertTag : function(tg)
13290     {
13291         // could be a bit smarter... -> wrap the current selected tRoo..
13292         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13293             
13294             range = this.createRange(this.getSelection());
13295             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13296             wrappingNode.appendChild(range.extractContents());
13297             range.insertNode(wrappingNode);
13298
13299             return;
13300             
13301             
13302             
13303         }
13304         this.execCmd("formatblock",   tg);
13305         
13306     },
13307     
13308     insertText : function(txt)
13309     {
13310         
13311         
13312         var range = this.createRange();
13313         range.deleteContents();
13314                //alert(Sender.getAttribute('label'));
13315                
13316         range.insertNode(this.doc.createTextNode(txt));
13317     } ,
13318     
13319      
13320
13321     /**
13322      * Executes a Midas editor command on the editor document and performs necessary focus and
13323      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13324      * @param {String} cmd The Midas command
13325      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13326      */
13327     relayCmd : function(cmd, value){
13328         this.win.focus();
13329         this.execCmd(cmd, value);
13330         this.owner.fireEvent('editorevent', this);
13331         //this.updateToolbar();
13332         this.owner.deferFocus();
13333     },
13334
13335     /**
13336      * Executes a Midas editor command directly on the editor document.
13337      * For visual commands, you should use {@link #relayCmd} instead.
13338      * <b>This should only be called after the editor is initialized.</b>
13339      * @param {String} cmd The Midas command
13340      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13341      */
13342     execCmd : function(cmd, value){
13343         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13344         this.syncValue();
13345     },
13346  
13347  
13348    
13349     /**
13350      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13351      * to insert tRoo.
13352      * @param {String} text | dom node.. 
13353      */
13354     insertAtCursor : function(text)
13355     {
13356         
13357         
13358         
13359         if(!this.activated){
13360             return;
13361         }
13362         /*
13363         if(Roo.isIE){
13364             this.win.focus();
13365             var r = this.doc.selection.createRange();
13366             if(r){
13367                 r.collapse(true);
13368                 r.pasteHTML(text);
13369                 this.syncValue();
13370                 this.deferFocus();
13371             
13372             }
13373             return;
13374         }
13375         */
13376         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13377             this.win.focus();
13378             
13379             
13380             // from jquery ui (MIT licenced)
13381             var range, node;
13382             var win = this.win;
13383             
13384             if (win.getSelection && win.getSelection().getRangeAt) {
13385                 range = win.getSelection().getRangeAt(0);
13386                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13387                 range.insertNode(node);
13388             } else if (win.document.selection && win.document.selection.createRange) {
13389                 // no firefox support
13390                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13391                 win.document.selection.createRange().pasteHTML(txt);
13392             } else {
13393                 // no firefox support
13394                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13395                 this.execCmd('InsertHTML', txt);
13396             } 
13397             
13398             this.syncValue();
13399             
13400             this.deferFocus();
13401         }
13402     },
13403  // private
13404     mozKeyPress : function(e){
13405         if(e.ctrlKey){
13406             var c = e.getCharCode(), cmd;
13407           
13408             if(c > 0){
13409                 c = String.fromCharCode(c).toLowerCase();
13410                 switch(c){
13411                     case 'b':
13412                         cmd = 'bold';
13413                         break;
13414                     case 'i':
13415                         cmd = 'italic';
13416                         break;
13417                     
13418                     case 'u':
13419                         cmd = 'underline';
13420                         break;
13421                     
13422                     case 'v':
13423                         this.cleanUpPaste.defer(100, this);
13424                         return;
13425                         
13426                 }
13427                 if(cmd){
13428                     this.win.focus();
13429                     this.execCmd(cmd);
13430                     this.deferFocus();
13431                     e.preventDefault();
13432                 }
13433                 
13434             }
13435         }
13436     },
13437
13438     // private
13439     fixKeys : function(){ // load time branching for fastest keydown performance
13440         if(Roo.isIE){
13441             return function(e){
13442                 var k = e.getKey(), r;
13443                 if(k == e.TAB){
13444                     e.stopEvent();
13445                     r = this.doc.selection.createRange();
13446                     if(r){
13447                         r.collapse(true);
13448                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13449                         this.deferFocus();
13450                     }
13451                     return;
13452                 }
13453                 
13454                 if(k == e.ENTER){
13455                     r = this.doc.selection.createRange();
13456                     if(r){
13457                         var target = r.parentElement();
13458                         if(!target || target.tagName.toLowerCase() != 'li'){
13459                             e.stopEvent();
13460                             r.pasteHTML('<br />');
13461                             r.collapse(false);
13462                             r.select();
13463                         }
13464                     }
13465                 }
13466                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13467                     this.cleanUpPaste.defer(100, this);
13468                     return;
13469                 }
13470                 
13471                 
13472             };
13473         }else if(Roo.isOpera){
13474             return function(e){
13475                 var k = e.getKey();
13476                 if(k == e.TAB){
13477                     e.stopEvent();
13478                     this.win.focus();
13479                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13480                     this.deferFocus();
13481                 }
13482                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13483                     this.cleanUpPaste.defer(100, this);
13484                     return;
13485                 }
13486                 
13487             };
13488         }else if(Roo.isSafari){
13489             return function(e){
13490                 var k = e.getKey();
13491                 
13492                 if(k == e.TAB){
13493                     e.stopEvent();
13494                     this.execCmd('InsertText','\t');
13495                     this.deferFocus();
13496                     return;
13497                 }
13498                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13499                     this.cleanUpPaste.defer(100, this);
13500                     return;
13501                 }
13502                 
13503              };
13504         }
13505     }(),
13506     
13507     getAllAncestors: function()
13508     {
13509         var p = this.getSelectedNode();
13510         var a = [];
13511         if (!p) {
13512             a.push(p); // push blank onto stack..
13513             p = this.getParentElement();
13514         }
13515         
13516         
13517         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13518             a.push(p);
13519             p = p.parentNode;
13520         }
13521         a.push(this.doc.body);
13522         return a;
13523     },
13524     lastSel : false,
13525     lastSelNode : false,
13526     
13527     
13528     getSelection : function() 
13529     {
13530         this.assignDocWin();
13531         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13532     },
13533     
13534     getSelectedNode: function() 
13535     {
13536         // this may only work on Gecko!!!
13537         
13538         // should we cache this!!!!
13539         
13540         
13541         
13542          
13543         var range = this.createRange(this.getSelection()).cloneRange();
13544         
13545         if (Roo.isIE) {
13546             var parent = range.parentElement();
13547             while (true) {
13548                 var testRange = range.duplicate();
13549                 testRange.moveToElementText(parent);
13550                 if (testRange.inRange(range)) {
13551                     break;
13552                 }
13553                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13554                     break;
13555                 }
13556                 parent = parent.parentElement;
13557             }
13558             return parent;
13559         }
13560         
13561         // is ancestor a text element.
13562         var ac =  range.commonAncestorContainer;
13563         if (ac.nodeType == 3) {
13564             ac = ac.parentNode;
13565         }
13566         
13567         var ar = ac.childNodes;
13568          
13569         var nodes = [];
13570         var other_nodes = [];
13571         var has_other_nodes = false;
13572         for (var i=0;i<ar.length;i++) {
13573             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13574                 continue;
13575             }
13576             // fullly contained node.
13577             
13578             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13579                 nodes.push(ar[i]);
13580                 continue;
13581             }
13582             
13583             // probably selected..
13584             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13585                 other_nodes.push(ar[i]);
13586                 continue;
13587             }
13588             // outer..
13589             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13590                 continue;
13591             }
13592             
13593             
13594             has_other_nodes = true;
13595         }
13596         if (!nodes.length && other_nodes.length) {
13597             nodes= other_nodes;
13598         }
13599         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13600             return false;
13601         }
13602         
13603         return nodes[0];
13604     },
13605     createRange: function(sel)
13606     {
13607         // this has strange effects when using with 
13608         // top toolbar - not sure if it's a great idea.
13609         //this.editor.contentWindow.focus();
13610         if (typeof sel != "undefined") {
13611             try {
13612                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13613             } catch(e) {
13614                 return this.doc.createRange();
13615             }
13616         } else {
13617             return this.doc.createRange();
13618         }
13619     },
13620     getParentElement: function()
13621     {
13622         
13623         this.assignDocWin();
13624         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13625         
13626         var range = this.createRange(sel);
13627          
13628         try {
13629             var p = range.commonAncestorContainer;
13630             while (p.nodeType == 3) { // text node
13631                 p = p.parentNode;
13632             }
13633             return p;
13634         } catch (e) {
13635             return null;
13636         }
13637     
13638     },
13639     /***
13640      *
13641      * Range intersection.. the hard stuff...
13642      *  '-1' = before
13643      *  '0' = hits..
13644      *  '1' = after.
13645      *         [ -- selected range --- ]
13646      *   [fail]                        [fail]
13647      *
13648      *    basically..
13649      *      if end is before start or  hits it. fail.
13650      *      if start is after end or hits it fail.
13651      *
13652      *   if either hits (but other is outside. - then it's not 
13653      *   
13654      *    
13655      **/
13656     
13657     
13658     // @see http://www.thismuchiknow.co.uk/?p=64.
13659     rangeIntersectsNode : function(range, node)
13660     {
13661         var nodeRange = node.ownerDocument.createRange();
13662         try {
13663             nodeRange.selectNode(node);
13664         } catch (e) {
13665             nodeRange.selectNodeContents(node);
13666         }
13667     
13668         var rangeStartRange = range.cloneRange();
13669         rangeStartRange.collapse(true);
13670     
13671         var rangeEndRange = range.cloneRange();
13672         rangeEndRange.collapse(false);
13673     
13674         var nodeStartRange = nodeRange.cloneRange();
13675         nodeStartRange.collapse(true);
13676     
13677         var nodeEndRange = nodeRange.cloneRange();
13678         nodeEndRange.collapse(false);
13679     
13680         return rangeStartRange.compareBoundaryPoints(
13681                  Range.START_TO_START, nodeEndRange) == -1 &&
13682                rangeEndRange.compareBoundaryPoints(
13683                  Range.START_TO_START, nodeStartRange) == 1;
13684         
13685          
13686     },
13687     rangeCompareNode : function(range, node)
13688     {
13689         var nodeRange = node.ownerDocument.createRange();
13690         try {
13691             nodeRange.selectNode(node);
13692         } catch (e) {
13693             nodeRange.selectNodeContents(node);
13694         }
13695         
13696         
13697         range.collapse(true);
13698     
13699         nodeRange.collapse(true);
13700      
13701         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13702         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13703          
13704         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13705         
13706         var nodeIsBefore   =  ss == 1;
13707         var nodeIsAfter    = ee == -1;
13708         
13709         if (nodeIsBefore && nodeIsAfter)
13710             return 0; // outer
13711         if (!nodeIsBefore && nodeIsAfter)
13712             return 1; //right trailed.
13713         
13714         if (nodeIsBefore && !nodeIsAfter)
13715             return 2;  // left trailed.
13716         // fully contined.
13717         return 3;
13718     },
13719
13720     // private? - in a new class?
13721     cleanUpPaste :  function()
13722     {
13723         // cleans up the whole document..
13724          Roo.log('cleanuppaste');
13725         this.cleanUpChildren(this.doc.body);
13726         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13727         if (clean != this.doc.body.innerHTML) {
13728             this.doc.body.innerHTML = clean;
13729         }
13730         
13731     },
13732     
13733     cleanWordChars : function(input) {// change the chars to hex code
13734         var he = Roo.HtmlEditorCore;
13735         
13736         var output = input;
13737         Roo.each(he.swapCodes, function(sw) { 
13738             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13739             
13740             output = output.replace(swapper, sw[1]);
13741         });
13742         
13743         return output;
13744     },
13745     
13746     
13747     cleanUpChildren : function (n)
13748     {
13749         if (!n.childNodes.length) {
13750             return;
13751         }
13752         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13753            this.cleanUpChild(n.childNodes[i]);
13754         }
13755     },
13756     
13757     
13758         
13759     
13760     cleanUpChild : function (node)
13761     {
13762         var ed = this;
13763         //console.log(node);
13764         if (node.nodeName == "#text") {
13765             // clean up silly Windows -- stuff?
13766             return; 
13767         }
13768         if (node.nodeName == "#comment") {
13769             node.parentNode.removeChild(node);
13770             // clean up silly Windows -- stuff?
13771             return; 
13772         }
13773         
13774         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
13775             // remove node.
13776             node.parentNode.removeChild(node);
13777             return;
13778             
13779         }
13780         
13781         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13782         
13783         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13784         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13785         
13786         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13787         //    remove_keep_children = true;
13788         //}
13789         
13790         if (remove_keep_children) {
13791             this.cleanUpChildren(node);
13792             // inserts everything just before this node...
13793             while (node.childNodes.length) {
13794                 var cn = node.childNodes[0];
13795                 node.removeChild(cn);
13796                 node.parentNode.insertBefore(cn, node);
13797             }
13798             node.parentNode.removeChild(node);
13799             return;
13800         }
13801         
13802         if (!node.attributes || !node.attributes.length) {
13803             this.cleanUpChildren(node);
13804             return;
13805         }
13806         
13807         function cleanAttr(n,v)
13808         {
13809             
13810             if (v.match(/^\./) || v.match(/^\//)) {
13811                 return;
13812             }
13813             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13814                 return;
13815             }
13816             if (v.match(/^#/)) {
13817                 return;
13818             }
13819 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13820             node.removeAttribute(n);
13821             
13822         }
13823         
13824         function cleanStyle(n,v)
13825         {
13826             if (v.match(/expression/)) { //XSS?? should we even bother..
13827                 node.removeAttribute(n);
13828                 return;
13829             }
13830             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13831             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13832             
13833             
13834             var parts = v.split(/;/);
13835             var clean = [];
13836             
13837             Roo.each(parts, function(p) {
13838                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13839                 if (!p.length) {
13840                     return true;
13841                 }
13842                 var l = p.split(':').shift().replace(/\s+/g,'');
13843                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13844                 
13845                 
13846                 if ( cblack.indexOf(l) > -1) {
13847 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13848                     //node.removeAttribute(n);
13849                     return true;
13850                 }
13851                 //Roo.log()
13852                 // only allow 'c whitelisted system attributes'
13853                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13854 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13855                     //node.removeAttribute(n);
13856                     return true;
13857                 }
13858                 
13859                 
13860                  
13861                 
13862                 clean.push(p);
13863                 return true;
13864             });
13865             if (clean.length) { 
13866                 node.setAttribute(n, clean.join(';'));
13867             } else {
13868                 node.removeAttribute(n);
13869             }
13870             
13871         }
13872         
13873         
13874         for (var i = node.attributes.length-1; i > -1 ; i--) {
13875             var a = node.attributes[i];
13876             //console.log(a);
13877             
13878             if (a.name.toLowerCase().substr(0,2)=='on')  {
13879                 node.removeAttribute(a.name);
13880                 continue;
13881             }
13882             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13883                 node.removeAttribute(a.name);
13884                 continue;
13885             }
13886             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13887                 cleanAttr(a.name,a.value); // fixme..
13888                 continue;
13889             }
13890             if (a.name == 'style') {
13891                 cleanStyle(a.name,a.value);
13892                 continue;
13893             }
13894             /// clean up MS crap..
13895             // tecnically this should be a list of valid class'es..
13896             
13897             
13898             if (a.name == 'class') {
13899                 if (a.value.match(/^Mso/)) {
13900                     node.className = '';
13901                 }
13902                 
13903                 if (a.value.match(/body/)) {
13904                     node.className = '';
13905                 }
13906                 continue;
13907             }
13908             
13909             // style cleanup!?
13910             // class cleanup?
13911             
13912         }
13913         
13914         
13915         this.cleanUpChildren(node);
13916         
13917         
13918     }
13919     
13920     
13921     // hide stuff that is not compatible
13922     /**
13923      * @event blur
13924      * @hide
13925      */
13926     /**
13927      * @event change
13928      * @hide
13929      */
13930     /**
13931      * @event focus
13932      * @hide
13933      */
13934     /**
13935      * @event specialkey
13936      * @hide
13937      */
13938     /**
13939      * @cfg {String} fieldClass @hide
13940      */
13941     /**
13942      * @cfg {String} focusClass @hide
13943      */
13944     /**
13945      * @cfg {String} autoCreate @hide
13946      */
13947     /**
13948      * @cfg {String} inputType @hide
13949      */
13950     /**
13951      * @cfg {String} invalidClass @hide
13952      */
13953     /**
13954      * @cfg {String} invalidText @hide
13955      */
13956     /**
13957      * @cfg {String} msgFx @hide
13958      */
13959     /**
13960      * @cfg {String} validateOnBlur @hide
13961      */
13962 });
13963
13964 Roo.HtmlEditorCore.white = [
13965         'area', 'br', 'img', 'input', 'hr', 'wbr',
13966         
13967        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
13968        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
13969        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
13970        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
13971        'table',   'ul',         'xmp', 
13972        
13973        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
13974       'thead',   'tr', 
13975      
13976       'dir', 'menu', 'ol', 'ul', 'dl',
13977        
13978       'embed',  'object'
13979 ];
13980
13981
13982 Roo.HtmlEditorCore.black = [
13983     //    'embed',  'object', // enable - backend responsiblity to clean thiese
13984         'applet', // 
13985         'base',   'basefont', 'bgsound', 'blink',  'body', 
13986         'frame',  'frameset', 'head',    'html',   'ilayer', 
13987         'iframe', 'layer',  'link',     'meta',    'object',   
13988         'script', 'style' ,'title',  'xml' // clean later..
13989 ];
13990 Roo.HtmlEditorCore.clean = [
13991     'script', 'style', 'title', 'xml'
13992 ];
13993 Roo.HtmlEditorCore.remove = [
13994     'font'
13995 ];
13996 // attributes..
13997
13998 Roo.HtmlEditorCore.ablack = [
13999     'on'
14000 ];
14001     
14002 Roo.HtmlEditorCore.aclean = [ 
14003     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14004 ];
14005
14006 // protocols..
14007 Roo.HtmlEditorCore.pwhite= [
14008         'http',  'https',  'mailto'
14009 ];
14010
14011 // white listed style attributes.
14012 Roo.HtmlEditorCore.cwhite= [
14013       //  'text-align', /// default is to allow most things..
14014       
14015          
14016 //        'font-size'//??
14017 ];
14018
14019 // black listed style attributes.
14020 Roo.HtmlEditorCore.cblack= [
14021       //  'font-size' -- this can be set by the project 
14022 ];
14023
14024
14025 Roo.HtmlEditorCore.swapCodes   =[ 
14026     [    8211, "--" ], 
14027     [    8212, "--" ], 
14028     [    8216,  "'" ],  
14029     [    8217, "'" ],  
14030     [    8220, '"' ],  
14031     [    8221, '"' ],  
14032     [    8226, "*" ],  
14033     [    8230, "..." ]
14034 ]; 
14035
14036     /*
14037  * - LGPL
14038  *
14039  * HtmlEditor
14040  * 
14041  */
14042
14043 /**
14044  * @class Roo.bootstrap.HtmlEditor
14045  * @extends Roo.bootstrap.TextArea
14046  * Bootstrap HtmlEditor class
14047
14048  * @constructor
14049  * Create a new HtmlEditor
14050  * @param {Object} config The config object
14051  */
14052
14053 Roo.bootstrap.HtmlEditor = function(config){
14054     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14055     if (!this.toolbars) {
14056         this.toolbars = [];
14057     }
14058     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14059     this.addEvents({
14060             /**
14061              * @event initialize
14062              * Fires when the editor is fully initialized (including the iframe)
14063              * @param {HtmlEditor} this
14064              */
14065             initialize: true,
14066             /**
14067              * @event activate
14068              * Fires when the editor is first receives the focus. Any insertion must wait
14069              * until after this event.
14070              * @param {HtmlEditor} this
14071              */
14072             activate: true,
14073              /**
14074              * @event beforesync
14075              * Fires before the textarea is updated with content from the editor iframe. Return false
14076              * to cancel the sync.
14077              * @param {HtmlEditor} this
14078              * @param {String} html
14079              */
14080             beforesync: true,
14081              /**
14082              * @event beforepush
14083              * Fires before the iframe editor is updated with content from the textarea. Return false
14084              * to cancel the push.
14085              * @param {HtmlEditor} this
14086              * @param {String} html
14087              */
14088             beforepush: true,
14089              /**
14090              * @event sync
14091              * Fires when the textarea is updated with content from the editor iframe.
14092              * @param {HtmlEditor} this
14093              * @param {String} html
14094              */
14095             sync: true,
14096              /**
14097              * @event push
14098              * Fires when the iframe editor is updated with content from the textarea.
14099              * @param {HtmlEditor} this
14100              * @param {String} html
14101              */
14102             push: true,
14103              /**
14104              * @event editmodechange
14105              * Fires when the editor switches edit modes
14106              * @param {HtmlEditor} this
14107              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14108              */
14109             editmodechange: true,
14110             /**
14111              * @event editorevent
14112              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14113              * @param {HtmlEditor} this
14114              */
14115             editorevent: true,
14116             /**
14117              * @event firstfocus
14118              * Fires when on first focus - needed by toolbars..
14119              * @param {HtmlEditor} this
14120              */
14121             firstfocus: true,
14122             /**
14123              * @event autosave
14124              * Auto save the htmlEditor value as a file into Events
14125              * @param {HtmlEditor} this
14126              */
14127             autosave: true,
14128             /**
14129              * @event savedpreview
14130              * preview the saved version of htmlEditor
14131              * @param {HtmlEditor} this
14132              */
14133             savedpreview: true
14134         });
14135 };
14136
14137
14138 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14139     
14140     
14141       /**
14142      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14143      */
14144     toolbars : false,
14145    
14146      /**
14147      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14148      *                        Roo.resizable.
14149      */
14150     resizable : false,
14151      /**
14152      * @cfg {Number} height (in pixels)
14153      */   
14154     height: 300,
14155    /**
14156      * @cfg {Number} width (in pixels)
14157      */   
14158     width: false,
14159     
14160     /**
14161      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14162      * 
14163      */
14164     stylesheets: false,
14165     
14166     // id of frame..
14167     frameId: false,
14168     
14169     // private properties
14170     validationEvent : false,
14171     deferHeight: true,
14172     initialized : false,
14173     activated : false,
14174     
14175     onFocus : Roo.emptyFn,
14176     iframePad:3,
14177     hideMode:'offsets',
14178     
14179     
14180     tbContainer : false,
14181     
14182     toolbarContainer :function() {
14183         return this.wrap.select('.x-html-editor-tb',true).first();
14184     },
14185
14186     /**
14187      * Protected method that will not generally be called directly. It
14188      * is called when the editor creates its toolbar. Override this method if you need to
14189      * add custom toolbar buttons.
14190      * @param {HtmlEditor} editor
14191      */
14192     createToolbar : function(){
14193         
14194         Roo.log("create toolbars");
14195         
14196         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14197         this.toolbars[0].render(this.toolbarContainer());
14198         
14199         return;
14200         
14201 //        if (!editor.toolbars || !editor.toolbars.length) {
14202 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14203 //        }
14204 //        
14205 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14206 //            editor.toolbars[i] = Roo.factory(
14207 //                    typeof(editor.toolbars[i]) == 'string' ?
14208 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14209 //                Roo.bootstrap.HtmlEditor);
14210 //            editor.toolbars[i].init(editor);
14211 //        }
14212     },
14213
14214      
14215     // private
14216     onRender : function(ct, position)
14217     {
14218        // Roo.log("Call onRender: " + this.xtype);
14219         var _t = this;
14220         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14221       
14222         this.wrap = this.inputEl().wrap({
14223             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14224         });
14225         
14226         this.editorcore.onRender(ct, position);
14227          
14228         if (this.resizable) {
14229             this.resizeEl = new Roo.Resizable(this.wrap, {
14230                 pinned : true,
14231                 wrap: true,
14232                 dynamic : true,
14233                 minHeight : this.height,
14234                 height: this.height,
14235                 handles : this.resizable,
14236                 width: this.width,
14237                 listeners : {
14238                     resize : function(r, w, h) {
14239                         _t.onResize(w,h); // -something
14240                     }
14241                 }
14242             });
14243             
14244         }
14245         this.createToolbar(this);
14246        
14247         
14248         if(!this.width && this.resizable){
14249             this.setSize(this.wrap.getSize());
14250         }
14251         if (this.resizeEl) {
14252             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14253             // should trigger onReize..
14254         }
14255         
14256     },
14257
14258     // private
14259     onResize : function(w, h)
14260     {
14261         Roo.log('resize: ' +w + ',' + h );
14262         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14263         var ew = false;
14264         var eh = false;
14265         
14266         if(this.inputEl() ){
14267             if(typeof w == 'number'){
14268                 var aw = w - this.wrap.getFrameWidth('lr');
14269                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14270                 ew = aw;
14271             }
14272             if(typeof h == 'number'){
14273                  var tbh = -11;  // fixme it needs to tool bar size!
14274                 for (var i =0; i < this.toolbars.length;i++) {
14275                     // fixme - ask toolbars for heights?
14276                     tbh += this.toolbars[i].el.getHeight();
14277                     //if (this.toolbars[i].footer) {
14278                     //    tbh += this.toolbars[i].footer.el.getHeight();
14279                     //}
14280                 }
14281               
14282                 
14283                 
14284                 
14285                 
14286                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14287                 ah -= 5; // knock a few pixes off for look..
14288                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14289                 var eh = ah;
14290             }
14291         }
14292         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14293         this.editorcore.onResize(ew,eh);
14294         
14295     },
14296
14297     /**
14298      * Toggles the editor between standard and source edit mode.
14299      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14300      */
14301     toggleSourceEdit : function(sourceEditMode)
14302     {
14303         this.editorcore.toggleSourceEdit(sourceEditMode);
14304         
14305         if(this.editorcore.sourceEditMode){
14306             Roo.log('editor - showing textarea');
14307             
14308 //            Roo.log('in');
14309 //            Roo.log(this.syncValue());
14310             this.syncValue();
14311             this.inputEl().removeClass('hide');
14312             this.inputEl().dom.removeAttribute('tabIndex');
14313             this.inputEl().focus();
14314         }else{
14315             Roo.log('editor - hiding textarea');
14316 //            Roo.log('out')
14317 //            Roo.log(this.pushValue()); 
14318             this.pushValue();
14319             
14320             this.inputEl().addClass('hide');
14321             this.inputEl().dom.setAttribute('tabIndex', -1);
14322             //this.deferFocus();
14323         }
14324          
14325         if(this.resizable){
14326             this.setSize(this.wrap.getSize());
14327         }
14328         
14329         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14330     },
14331  
14332     // private (for BoxComponent)
14333     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14334
14335     // private (for BoxComponent)
14336     getResizeEl : function(){
14337         return this.wrap;
14338     },
14339
14340     // private (for BoxComponent)
14341     getPositionEl : function(){
14342         return this.wrap;
14343     },
14344
14345     // private
14346     initEvents : function(){
14347         this.originalValue = this.getValue();
14348     },
14349
14350 //    /**
14351 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14352 //     * @method
14353 //     */
14354 //    markInvalid : Roo.emptyFn,
14355 //    /**
14356 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14357 //     * @method
14358 //     */
14359 //    clearInvalid : Roo.emptyFn,
14360
14361     setValue : function(v){
14362         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14363         this.editorcore.pushValue();
14364     },
14365
14366      
14367     // private
14368     deferFocus : function(){
14369         this.focus.defer(10, this);
14370     },
14371
14372     // doc'ed in Field
14373     focus : function(){
14374         this.editorcore.focus();
14375         
14376     },
14377       
14378
14379     // private
14380     onDestroy : function(){
14381         
14382         
14383         
14384         if(this.rendered){
14385             
14386             for (var i =0; i < this.toolbars.length;i++) {
14387                 // fixme - ask toolbars for heights?
14388                 this.toolbars[i].onDestroy();
14389             }
14390             
14391             this.wrap.dom.innerHTML = '';
14392             this.wrap.remove();
14393         }
14394     },
14395
14396     // private
14397     onFirstFocus : function(){
14398         //Roo.log("onFirstFocus");
14399         this.editorcore.onFirstFocus();
14400          for (var i =0; i < this.toolbars.length;i++) {
14401             this.toolbars[i].onFirstFocus();
14402         }
14403         
14404     },
14405     
14406     // private
14407     syncValue : function()
14408     {   
14409         this.editorcore.syncValue();
14410     },
14411     
14412     pushValue : function()
14413     {   
14414         this.editorcore.pushValue();
14415     }
14416      
14417     
14418     // hide stuff that is not compatible
14419     /**
14420      * @event blur
14421      * @hide
14422      */
14423     /**
14424      * @event change
14425      * @hide
14426      */
14427     /**
14428      * @event focus
14429      * @hide
14430      */
14431     /**
14432      * @event specialkey
14433      * @hide
14434      */
14435     /**
14436      * @cfg {String} fieldClass @hide
14437      */
14438     /**
14439      * @cfg {String} focusClass @hide
14440      */
14441     /**
14442      * @cfg {String} autoCreate @hide
14443      */
14444     /**
14445      * @cfg {String} inputType @hide
14446      */
14447     /**
14448      * @cfg {String} invalidClass @hide
14449      */
14450     /**
14451      * @cfg {String} invalidText @hide
14452      */
14453     /**
14454      * @cfg {String} msgFx @hide
14455      */
14456     /**
14457      * @cfg {String} validateOnBlur @hide
14458      */
14459 });
14460  
14461     
14462    
14463    
14464    
14465       
14466
14467 /**
14468  * @class Roo.bootstrap.HtmlEditorToolbar1
14469  * Basic Toolbar
14470  * 
14471  * Usage:
14472  *
14473  new Roo.bootstrap.HtmlEditor({
14474     ....
14475     toolbars : [
14476         new Roo.bootstrap.HtmlEditorToolbar1({
14477             disable : { fonts: 1 , format: 1, ..., ... , ...],
14478             btns : [ .... ]
14479         })
14480     }
14481      
14482  * 
14483  * @cfg {Object} disable List of elements to disable..
14484  * @cfg {Array} btns List of additional buttons.
14485  * 
14486  * 
14487  * NEEDS Extra CSS? 
14488  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14489  */
14490  
14491 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14492 {
14493     
14494     Roo.apply(this, config);
14495     
14496     // default disabled, based on 'good practice'..
14497     this.disable = this.disable || {};
14498     Roo.applyIf(this.disable, {
14499         fontSize : true,
14500         colors : true,
14501         specialElements : true
14502     });
14503     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14504     
14505     this.editor = config.editor;
14506     this.editorcore = config.editor.editorcore;
14507     
14508     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14509     
14510     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14511     // dont call parent... till later.
14512 }
14513 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14514     
14515     
14516     bar : true,
14517     
14518     editor : false,
14519     editorcore : false,
14520     
14521     
14522     formats : [
14523         "p" ,  
14524         "h1","h2","h3","h4","h5","h6", 
14525         "pre", "code", 
14526         "abbr", "acronym", "address", "cite", "samp", "var",
14527         'div','span'
14528     ],
14529     
14530     onRender : function(ct, position)
14531     {
14532        // Roo.log("Call onRender: " + this.xtype);
14533         
14534        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14535        Roo.log(this.el);
14536        this.el.dom.style.marginBottom = '0';
14537        var _this = this;
14538        var editorcore = this.editorcore;
14539        var editor= this.editor;
14540        
14541        var children = [];
14542        var btn = function(id,cmd , toggle, handler){
14543        
14544             var  event = toggle ? 'toggle' : 'click';
14545        
14546             var a = {
14547                 size : 'sm',
14548                 xtype: 'Button',
14549                 xns: Roo.bootstrap,
14550                 glyphicon : id,
14551                 cmd : id || cmd,
14552                 enableToggle:toggle !== false,
14553                 //html : 'submit'
14554                 pressed : toggle ? false : null,
14555                 listeners : {}
14556             }
14557             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14558                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14559             }
14560             children.push(a);
14561             return a;
14562        }
14563         
14564         var style = {
14565                 xtype: 'Button',
14566                 size : 'sm',
14567                 xns: Roo.bootstrap,
14568                 glyphicon : 'font',
14569                 //html : 'submit'
14570                 menu : {
14571                     xtype: 'Menu',
14572                     xns: Roo.bootstrap,
14573                     items:  []
14574                 }
14575         };
14576         Roo.each(this.formats, function(f) {
14577             style.menu.items.push({
14578                 xtype :'MenuItem',
14579                 xns: Roo.bootstrap,
14580                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14581                 tagname : f,
14582                 listeners : {
14583                     click : function()
14584                     {
14585                         editorcore.insertTag(this.tagname);
14586                         editor.focus();
14587                     }
14588                 }
14589                 
14590             });
14591         });
14592          children.push(style);   
14593             
14594             
14595         btn('bold',false,true);
14596         btn('italic',false,true);
14597         btn('align-left', 'justifyleft',true);
14598         btn('align-center', 'justifycenter',true);
14599         btn('align-right' , 'justifyright',true);
14600         btn('link', false, false, function(btn) {
14601             //Roo.log("create link?");
14602             var url = prompt(this.createLinkText, this.defaultLinkValue);
14603             if(url && url != 'http:/'+'/'){
14604                 this.editorcore.relayCmd('createlink', url);
14605             }
14606         }),
14607         btn('list','insertunorderedlist',true);
14608         btn('pencil', false,true, function(btn){
14609                 Roo.log(this);
14610                 
14611                 this.toggleSourceEdit(btn.pressed);
14612         });
14613         /*
14614         var cog = {
14615                 xtype: 'Button',
14616                 size : 'sm',
14617                 xns: Roo.bootstrap,
14618                 glyphicon : 'cog',
14619                 //html : 'submit'
14620                 menu : {
14621                     xtype: 'Menu',
14622                     xns: Roo.bootstrap,
14623                     items:  []
14624                 }
14625         };
14626         
14627         cog.menu.items.push({
14628             xtype :'MenuItem',
14629             xns: Roo.bootstrap,
14630             html : Clean styles,
14631             tagname : f,
14632             listeners : {
14633                 click : function()
14634                 {
14635                     editorcore.insertTag(this.tagname);
14636                     editor.focus();
14637                 }
14638             }
14639             
14640         });
14641        */
14642         
14643          
14644        this.xtype = 'Navbar';
14645         
14646         for(var i=0;i< children.length;i++) {
14647             
14648             this.buttons.add(this.addxtypeChild(children[i]));
14649             
14650         }
14651         
14652         editor.on('editorevent', this.updateToolbar, this);
14653     },
14654     onBtnClick : function(id)
14655     {
14656        this.editorcore.relayCmd(id);
14657        this.editorcore.focus();
14658     },
14659     
14660     /**
14661      * Protected method that will not generally be called directly. It triggers
14662      * a toolbar update by reading the markup state of the current selection in the editor.
14663      */
14664     updateToolbar: function(){
14665
14666         if(!this.editorcore.activated){
14667             this.editor.onFirstFocus(); // is this neeed?
14668             return;
14669         }
14670
14671         var btns = this.buttons; 
14672         var doc = this.editorcore.doc;
14673         btns.get('bold').setActive(doc.queryCommandState('bold'));
14674         btns.get('italic').setActive(doc.queryCommandState('italic'));
14675         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14676         
14677         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14678         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14679         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14680         
14681         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14682         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14683          /*
14684         
14685         var ans = this.editorcore.getAllAncestors();
14686         if (this.formatCombo) {
14687             
14688             
14689             var store = this.formatCombo.store;
14690             this.formatCombo.setValue("");
14691             for (var i =0; i < ans.length;i++) {
14692                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14693                     // select it..
14694                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14695                     break;
14696                 }
14697             }
14698         }
14699         
14700         
14701         
14702         // hides menus... - so this cant be on a menu...
14703         Roo.bootstrap.MenuMgr.hideAll();
14704         */
14705         Roo.bootstrap.MenuMgr.hideAll();
14706         //this.editorsyncValue();
14707     },
14708     onFirstFocus: function() {
14709         this.buttons.each(function(item){
14710            item.enable();
14711         });
14712     },
14713     toggleSourceEdit : function(sourceEditMode){
14714         
14715           
14716         if(sourceEditMode){
14717             Roo.log("disabling buttons");
14718            this.buttons.each( function(item){
14719                 if(item.cmd != 'pencil'){
14720                     item.disable();
14721                 }
14722             });
14723           
14724         }else{
14725             Roo.log("enabling buttons");
14726             if(this.editorcore.initialized){
14727                 this.buttons.each( function(item){
14728                     item.enable();
14729                 });
14730             }
14731             
14732         }
14733         Roo.log("calling toggole on editor");
14734         // tell the editor that it's been pressed..
14735         this.editor.toggleSourceEdit(sourceEditMode);
14736        
14737     }
14738 });
14739
14740
14741
14742
14743
14744 /**
14745  * @class Roo.bootstrap.Table.AbstractSelectionModel
14746  * @extends Roo.util.Observable
14747  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14748  * implemented by descendant classes.  This class should not be directly instantiated.
14749  * @constructor
14750  */
14751 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14752     this.locked = false;
14753     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14754 };
14755
14756
14757 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14758     /** @ignore Called by the grid automatically. Do not call directly. */
14759     init : function(grid){
14760         this.grid = grid;
14761         this.initEvents();
14762     },
14763
14764     /**
14765      * Locks the selections.
14766      */
14767     lock : function(){
14768         this.locked = true;
14769     },
14770
14771     /**
14772      * Unlocks the selections.
14773      */
14774     unlock : function(){
14775         this.locked = false;
14776     },
14777
14778     /**
14779      * Returns true if the selections are locked.
14780      * @return {Boolean}
14781      */
14782     isLocked : function(){
14783         return this.locked;
14784     }
14785 });
14786 /**
14787  * @class Roo.bootstrap.Table.ColumnModel
14788  * @extends Roo.util.Observable
14789  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14790  * the columns in the table.
14791  
14792  * @constructor
14793  * @param {Object} config An Array of column config objects. See this class's
14794  * config objects for details.
14795 */
14796 Roo.bootstrap.Table.ColumnModel = function(config){
14797         /**
14798      * The config passed into the constructor
14799      */
14800     this.config = config;
14801     this.lookup = {};
14802
14803     // if no id, create one
14804     // if the column does not have a dataIndex mapping,
14805     // map it to the order it is in the config
14806     for(var i = 0, len = config.length; i < len; i++){
14807         var c = config[i];
14808         if(typeof c.dataIndex == "undefined"){
14809             c.dataIndex = i;
14810         }
14811         if(typeof c.renderer == "string"){
14812             c.renderer = Roo.util.Format[c.renderer];
14813         }
14814         if(typeof c.id == "undefined"){
14815             c.id = Roo.id();
14816         }
14817 //        if(c.editor && c.editor.xtype){
14818 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14819 //        }
14820 //        if(c.editor && c.editor.isFormField){
14821 //            c.editor = new Roo.grid.GridEditor(c.editor);
14822 //        }
14823
14824         this.lookup[c.id] = c;
14825     }
14826
14827     /**
14828      * The width of columns which have no width specified (defaults to 100)
14829      * @type Number
14830      */
14831     this.defaultWidth = 100;
14832
14833     /**
14834      * Default sortable of columns which have no sortable specified (defaults to false)
14835      * @type Boolean
14836      */
14837     this.defaultSortable = false;
14838
14839     this.addEvents({
14840         /**
14841              * @event widthchange
14842              * Fires when the width of a column changes.
14843              * @param {ColumnModel} this
14844              * @param {Number} columnIndex The column index
14845              * @param {Number} newWidth The new width
14846              */
14847             "widthchange": true,
14848         /**
14849              * @event headerchange
14850              * Fires when the text of a header changes.
14851              * @param {ColumnModel} this
14852              * @param {Number} columnIndex The column index
14853              * @param {Number} newText The new header text
14854              */
14855             "headerchange": true,
14856         /**
14857              * @event hiddenchange
14858              * Fires when a column is hidden or "unhidden".
14859              * @param {ColumnModel} this
14860              * @param {Number} columnIndex The column index
14861              * @param {Boolean} hidden true if hidden, false otherwise
14862              */
14863             "hiddenchange": true,
14864             /**
14865          * @event columnmoved
14866          * Fires when a column is moved.
14867          * @param {ColumnModel} this
14868          * @param {Number} oldIndex
14869          * @param {Number} newIndex
14870          */
14871         "columnmoved" : true,
14872         /**
14873          * @event columlockchange
14874          * Fires when a column's locked state is changed
14875          * @param {ColumnModel} this
14876          * @param {Number} colIndex
14877          * @param {Boolean} locked true if locked
14878          */
14879         "columnlockchange" : true
14880     });
14881     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14882 };
14883 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14884     /**
14885      * @cfg {String} header The header text to display in the Grid view.
14886      */
14887     /**
14888      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14889      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14890      * specified, the column's index is used as an index into the Record's data Array.
14891      */
14892     /**
14893      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14894      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14895      */
14896     /**
14897      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14898      * Defaults to the value of the {@link #defaultSortable} property.
14899      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14900      */
14901     /**
14902      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14903      */
14904     /**
14905      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14906      */
14907     /**
14908      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14909      */
14910     /**
14911      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14912      */
14913     /**
14914      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14915      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14916      * default renderer uses the raw data value.
14917      */
14918     /**
14919      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14920      */
14921
14922     /**
14923      * Returns the id of the column at the specified index.
14924      * @param {Number} index The column index
14925      * @return {String} the id
14926      */
14927     getColumnId : function(index){
14928         return this.config[index].id;
14929     },
14930
14931     /**
14932      * Returns the column for a specified id.
14933      * @param {String} id The column id
14934      * @return {Object} the column
14935      */
14936     getColumnById : function(id){
14937         return this.lookup[id];
14938     },
14939
14940     
14941     /**
14942      * Returns the column for a specified dataIndex.
14943      * @param {String} dataIndex The column dataIndex
14944      * @return {Object|Boolean} the column or false if not found
14945      */
14946     getColumnByDataIndex: function(dataIndex){
14947         var index = this.findColumnIndex(dataIndex);
14948         return index > -1 ? this.config[index] : false;
14949     },
14950     
14951     /**
14952      * Returns the index for a specified column id.
14953      * @param {String} id The column id
14954      * @return {Number} the index, or -1 if not found
14955      */
14956     getIndexById : function(id){
14957         for(var i = 0, len = this.config.length; i < len; i++){
14958             if(this.config[i].id == id){
14959                 return i;
14960             }
14961         }
14962         return -1;
14963     },
14964     
14965     /**
14966      * Returns the index for a specified column dataIndex.
14967      * @param {String} dataIndex The column dataIndex
14968      * @return {Number} the index, or -1 if not found
14969      */
14970     
14971     findColumnIndex : function(dataIndex){
14972         for(var i = 0, len = this.config.length; i < len; i++){
14973             if(this.config[i].dataIndex == dataIndex){
14974                 return i;
14975             }
14976         }
14977         return -1;
14978     },
14979     
14980     
14981     moveColumn : function(oldIndex, newIndex){
14982         var c = this.config[oldIndex];
14983         this.config.splice(oldIndex, 1);
14984         this.config.splice(newIndex, 0, c);
14985         this.dataMap = null;
14986         this.fireEvent("columnmoved", this, oldIndex, newIndex);
14987     },
14988
14989     isLocked : function(colIndex){
14990         return this.config[colIndex].locked === true;
14991     },
14992
14993     setLocked : function(colIndex, value, suppressEvent){
14994         if(this.isLocked(colIndex) == value){
14995             return;
14996         }
14997         this.config[colIndex].locked = value;
14998         if(!suppressEvent){
14999             this.fireEvent("columnlockchange", this, colIndex, value);
15000         }
15001     },
15002
15003     getTotalLockedWidth : function(){
15004         var totalWidth = 0;
15005         for(var i = 0; i < this.config.length; i++){
15006             if(this.isLocked(i) && !this.isHidden(i)){
15007                 this.totalWidth += this.getColumnWidth(i);
15008             }
15009         }
15010         return totalWidth;
15011     },
15012
15013     getLockedCount : function(){
15014         for(var i = 0, len = this.config.length; i < len; i++){
15015             if(!this.isLocked(i)){
15016                 return i;
15017             }
15018         }
15019     },
15020
15021     /**
15022      * Returns the number of columns.
15023      * @return {Number}
15024      */
15025     getColumnCount : function(visibleOnly){
15026         if(visibleOnly === true){
15027             var c = 0;
15028             for(var i = 0, len = this.config.length; i < len; i++){
15029                 if(!this.isHidden(i)){
15030                     c++;
15031                 }
15032             }
15033             return c;
15034         }
15035         return this.config.length;
15036     },
15037
15038     /**
15039      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15040      * @param {Function} fn
15041      * @param {Object} scope (optional)
15042      * @return {Array} result
15043      */
15044     getColumnsBy : function(fn, scope){
15045         var r = [];
15046         for(var i = 0, len = this.config.length; i < len; i++){
15047             var c = this.config[i];
15048             if(fn.call(scope||this, c, i) === true){
15049                 r[r.length] = c;
15050             }
15051         }
15052         return r;
15053     },
15054
15055     /**
15056      * Returns true if the specified column is sortable.
15057      * @param {Number} col The column index
15058      * @return {Boolean}
15059      */
15060     isSortable : function(col){
15061         if(typeof this.config[col].sortable == "undefined"){
15062             return this.defaultSortable;
15063         }
15064         return this.config[col].sortable;
15065     },
15066
15067     /**
15068      * Returns the rendering (formatting) function defined for the column.
15069      * @param {Number} col The column index.
15070      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15071      */
15072     getRenderer : function(col){
15073         if(!this.config[col].renderer){
15074             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15075         }
15076         return this.config[col].renderer;
15077     },
15078
15079     /**
15080      * Sets the rendering (formatting) function for a column.
15081      * @param {Number} col The column index
15082      * @param {Function} fn The function to use to process the cell's raw data
15083      * to return HTML markup for the grid view. The render function is called with
15084      * the following parameters:<ul>
15085      * <li>Data value.</li>
15086      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15087      * <li>css A CSS style string to apply to the table cell.</li>
15088      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15089      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15090      * <li>Row index</li>
15091      * <li>Column index</li>
15092      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15093      */
15094     setRenderer : function(col, fn){
15095         this.config[col].renderer = fn;
15096     },
15097
15098     /**
15099      * Returns the width for the specified column.
15100      * @param {Number} col The column index
15101      * @return {Number}
15102      */
15103     getColumnWidth : function(col){
15104         return this.config[col].width * 1 || this.defaultWidth;
15105     },
15106
15107     /**
15108      * Sets the width for a column.
15109      * @param {Number} col The column index
15110      * @param {Number} width The new width
15111      */
15112     setColumnWidth : function(col, width, suppressEvent){
15113         this.config[col].width = width;
15114         this.totalWidth = null;
15115         if(!suppressEvent){
15116              this.fireEvent("widthchange", this, col, width);
15117         }
15118     },
15119
15120     /**
15121      * Returns the total width of all columns.
15122      * @param {Boolean} includeHidden True to include hidden column widths
15123      * @return {Number}
15124      */
15125     getTotalWidth : function(includeHidden){
15126         if(!this.totalWidth){
15127             this.totalWidth = 0;
15128             for(var i = 0, len = this.config.length; i < len; i++){
15129                 if(includeHidden || !this.isHidden(i)){
15130                     this.totalWidth += this.getColumnWidth(i);
15131                 }
15132             }
15133         }
15134         return this.totalWidth;
15135     },
15136
15137     /**
15138      * Returns the header for the specified column.
15139      * @param {Number} col The column index
15140      * @return {String}
15141      */
15142     getColumnHeader : function(col){
15143         return this.config[col].header;
15144     },
15145
15146     /**
15147      * Sets the header for a column.
15148      * @param {Number} col The column index
15149      * @param {String} header The new header
15150      */
15151     setColumnHeader : function(col, header){
15152         this.config[col].header = header;
15153         this.fireEvent("headerchange", this, col, header);
15154     },
15155
15156     /**
15157      * Returns the tooltip for the specified column.
15158      * @param {Number} col The column index
15159      * @return {String}
15160      */
15161     getColumnTooltip : function(col){
15162             return this.config[col].tooltip;
15163     },
15164     /**
15165      * Sets the tooltip for a column.
15166      * @param {Number} col The column index
15167      * @param {String} tooltip The new tooltip
15168      */
15169     setColumnTooltip : function(col, tooltip){
15170             this.config[col].tooltip = tooltip;
15171     },
15172
15173     /**
15174      * Returns the dataIndex for the specified column.
15175      * @param {Number} col The column index
15176      * @return {Number}
15177      */
15178     getDataIndex : function(col){
15179         return this.config[col].dataIndex;
15180     },
15181
15182     /**
15183      * Sets the dataIndex for a column.
15184      * @param {Number} col The column index
15185      * @param {Number} dataIndex The new dataIndex
15186      */
15187     setDataIndex : function(col, dataIndex){
15188         this.config[col].dataIndex = dataIndex;
15189     },
15190
15191     
15192     
15193     /**
15194      * Returns true if the cell is editable.
15195      * @param {Number} colIndex The column index
15196      * @param {Number} rowIndex The row index
15197      * @return {Boolean}
15198      */
15199     isCellEditable : function(colIndex, rowIndex){
15200         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15201     },
15202
15203     /**
15204      * Returns the editor defined for the cell/column.
15205      * return false or null to disable editing.
15206      * @param {Number} colIndex The column index
15207      * @param {Number} rowIndex The row index
15208      * @return {Object}
15209      */
15210     getCellEditor : function(colIndex, rowIndex){
15211         return this.config[colIndex].editor;
15212     },
15213
15214     /**
15215      * Sets if a column is editable.
15216      * @param {Number} col The column index
15217      * @param {Boolean} editable True if the column is editable
15218      */
15219     setEditable : function(col, editable){
15220         this.config[col].editable = editable;
15221     },
15222
15223
15224     /**
15225      * Returns true if the column is hidden.
15226      * @param {Number} colIndex The column index
15227      * @return {Boolean}
15228      */
15229     isHidden : function(colIndex){
15230         return this.config[colIndex].hidden;
15231     },
15232
15233
15234     /**
15235      * Returns true if the column width cannot be changed
15236      */
15237     isFixed : function(colIndex){
15238         return this.config[colIndex].fixed;
15239     },
15240
15241     /**
15242      * Returns true if the column can be resized
15243      * @return {Boolean}
15244      */
15245     isResizable : function(colIndex){
15246         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15247     },
15248     /**
15249      * Sets if a column is hidden.
15250      * @param {Number} colIndex The column index
15251      * @param {Boolean} hidden True if the column is hidden
15252      */
15253     setHidden : function(colIndex, hidden){
15254         this.config[colIndex].hidden = hidden;
15255         this.totalWidth = null;
15256         this.fireEvent("hiddenchange", this, colIndex, hidden);
15257     },
15258
15259     /**
15260      * Sets the editor for a column.
15261      * @param {Number} col The column index
15262      * @param {Object} editor The editor object
15263      */
15264     setEditor : function(col, editor){
15265         this.config[col].editor = editor;
15266     }
15267 });
15268
15269 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15270         if(typeof value == "string" && value.length < 1){
15271             return "&#160;";
15272         }
15273         return value;
15274 };
15275
15276 // Alias for backwards compatibility
15277 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15278
15279 /**
15280  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15281  * @class Roo.bootstrap.Table.RowSelectionModel
15282  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15283  * It supports multiple selections and keyboard selection/navigation. 
15284  * @constructor
15285  * @param {Object} config
15286  */
15287
15288 Roo.bootstrap.Table.RowSelectionModel = function(config){
15289     Roo.apply(this, config);
15290     this.selections = new Roo.util.MixedCollection(false, function(o){
15291         return o.id;
15292     });
15293
15294     this.last = false;
15295     this.lastActive = false;
15296
15297     this.addEvents({
15298         /**
15299              * @event selectionchange
15300              * Fires when the selection changes
15301              * @param {SelectionModel} this
15302              */
15303             "selectionchange" : true,
15304         /**
15305              * @event afterselectionchange
15306              * Fires after the selection changes (eg. by key press or clicking)
15307              * @param {SelectionModel} this
15308              */
15309             "afterselectionchange" : true,
15310         /**
15311              * @event beforerowselect
15312              * Fires when a row is selected being selected, return false to cancel.
15313              * @param {SelectionModel} this
15314              * @param {Number} rowIndex The selected index
15315              * @param {Boolean} keepExisting False if other selections will be cleared
15316              */
15317             "beforerowselect" : true,
15318         /**
15319              * @event rowselect
15320              * Fires when a row is selected.
15321              * @param {SelectionModel} this
15322              * @param {Number} rowIndex The selected index
15323              * @param {Roo.data.Record} r The record
15324              */
15325             "rowselect" : true,
15326         /**
15327              * @event rowdeselect
15328              * Fires when a row is deselected.
15329              * @param {SelectionModel} this
15330              * @param {Number} rowIndex The selected index
15331              */
15332         "rowdeselect" : true
15333     });
15334     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15335     this.locked = false;
15336 };
15337
15338 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15339     /**
15340      * @cfg {Boolean} singleSelect
15341      * True to allow selection of only one row at a time (defaults to false)
15342      */
15343     singleSelect : false,
15344
15345     // private
15346     initEvents : function(){
15347
15348         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15349             this.grid.on("mousedown", this.handleMouseDown, this);
15350         }else{ // allow click to work like normal
15351             this.grid.on("rowclick", this.handleDragableRowClick, this);
15352         }
15353
15354         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15355             "up" : function(e){
15356                 if(!e.shiftKey){
15357                     this.selectPrevious(e.shiftKey);
15358                 }else if(this.last !== false && this.lastActive !== false){
15359                     var last = this.last;
15360                     this.selectRange(this.last,  this.lastActive-1);
15361                     this.grid.getView().focusRow(this.lastActive);
15362                     if(last !== false){
15363                         this.last = last;
15364                     }
15365                 }else{
15366                     this.selectFirstRow();
15367                 }
15368                 this.fireEvent("afterselectionchange", this);
15369             },
15370             "down" : function(e){
15371                 if(!e.shiftKey){
15372                     this.selectNext(e.shiftKey);
15373                 }else if(this.last !== false && this.lastActive !== false){
15374                     var last = this.last;
15375                     this.selectRange(this.last,  this.lastActive+1);
15376                     this.grid.getView().focusRow(this.lastActive);
15377                     if(last !== false){
15378                         this.last = last;
15379                     }
15380                 }else{
15381                     this.selectFirstRow();
15382                 }
15383                 this.fireEvent("afterselectionchange", this);
15384             },
15385             scope: this
15386         });
15387
15388         var view = this.grid.view;
15389         view.on("refresh", this.onRefresh, this);
15390         view.on("rowupdated", this.onRowUpdated, this);
15391         view.on("rowremoved", this.onRemove, this);
15392     },
15393
15394     // private
15395     onRefresh : function(){
15396         var ds = this.grid.dataSource, i, v = this.grid.view;
15397         var s = this.selections;
15398         s.each(function(r){
15399             if((i = ds.indexOfId(r.id)) != -1){
15400                 v.onRowSelect(i);
15401             }else{
15402                 s.remove(r);
15403             }
15404         });
15405     },
15406
15407     // private
15408     onRemove : function(v, index, r){
15409         this.selections.remove(r);
15410     },
15411
15412     // private
15413     onRowUpdated : function(v, index, r){
15414         if(this.isSelected(r)){
15415             v.onRowSelect(index);
15416         }
15417     },
15418
15419     /**
15420      * Select records.
15421      * @param {Array} records The records to select
15422      * @param {Boolean} keepExisting (optional) True to keep existing selections
15423      */
15424     selectRecords : function(records, keepExisting){
15425         if(!keepExisting){
15426             this.clearSelections();
15427         }
15428         var ds = this.grid.dataSource;
15429         for(var i = 0, len = records.length; i < len; i++){
15430             this.selectRow(ds.indexOf(records[i]), true);
15431         }
15432     },
15433
15434     /**
15435      * Gets the number of selected rows.
15436      * @return {Number}
15437      */
15438     getCount : function(){
15439         return this.selections.length;
15440     },
15441
15442     /**
15443      * Selects the first row in the grid.
15444      */
15445     selectFirstRow : function(){
15446         this.selectRow(0);
15447     },
15448
15449     /**
15450      * Select the last row.
15451      * @param {Boolean} keepExisting (optional) True to keep existing selections
15452      */
15453     selectLastRow : function(keepExisting){
15454         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15455     },
15456
15457     /**
15458      * Selects the row immediately following the last selected row.
15459      * @param {Boolean} keepExisting (optional) True to keep existing selections
15460      */
15461     selectNext : function(keepExisting){
15462         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15463             this.selectRow(this.last+1, keepExisting);
15464             this.grid.getView().focusRow(this.last);
15465         }
15466     },
15467
15468     /**
15469      * Selects the row that precedes the last selected row.
15470      * @param {Boolean} keepExisting (optional) True to keep existing selections
15471      */
15472     selectPrevious : function(keepExisting){
15473         if(this.last){
15474             this.selectRow(this.last-1, keepExisting);
15475             this.grid.getView().focusRow(this.last);
15476         }
15477     },
15478
15479     /**
15480      * Returns the selected records
15481      * @return {Array} Array of selected records
15482      */
15483     getSelections : function(){
15484         return [].concat(this.selections.items);
15485     },
15486
15487     /**
15488      * Returns the first selected record.
15489      * @return {Record}
15490      */
15491     getSelected : function(){
15492         return this.selections.itemAt(0);
15493     },
15494
15495
15496     /**
15497      * Clears all selections.
15498      */
15499     clearSelections : function(fast){
15500         if(this.locked) return;
15501         if(fast !== true){
15502             var ds = this.grid.dataSource;
15503             var s = this.selections;
15504             s.each(function(r){
15505                 this.deselectRow(ds.indexOfId(r.id));
15506             }, this);
15507             s.clear();
15508         }else{
15509             this.selections.clear();
15510         }
15511         this.last = false;
15512     },
15513
15514
15515     /**
15516      * Selects all rows.
15517      */
15518     selectAll : function(){
15519         if(this.locked) return;
15520         this.selections.clear();
15521         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15522             this.selectRow(i, true);
15523         }
15524     },
15525
15526     /**
15527      * Returns True if there is a selection.
15528      * @return {Boolean}
15529      */
15530     hasSelection : function(){
15531         return this.selections.length > 0;
15532     },
15533
15534     /**
15535      * Returns True if the specified row is selected.
15536      * @param {Number/Record} record The record or index of the record to check
15537      * @return {Boolean}
15538      */
15539     isSelected : function(index){
15540         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15541         return (r && this.selections.key(r.id) ? true : false);
15542     },
15543
15544     /**
15545      * Returns True if the specified record id is selected.
15546      * @param {String} id The id of record to check
15547      * @return {Boolean}
15548      */
15549     isIdSelected : function(id){
15550         return (this.selections.key(id) ? true : false);
15551     },
15552
15553     // private
15554     handleMouseDown : function(e, t){
15555         var view = this.grid.getView(), rowIndex;
15556         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15557             return;
15558         };
15559         if(e.shiftKey && this.last !== false){
15560             var last = this.last;
15561             this.selectRange(last, rowIndex, e.ctrlKey);
15562             this.last = last; // reset the last
15563             view.focusRow(rowIndex);
15564         }else{
15565             var isSelected = this.isSelected(rowIndex);
15566             if(e.button !== 0 && isSelected){
15567                 view.focusRow(rowIndex);
15568             }else if(e.ctrlKey && isSelected){
15569                 this.deselectRow(rowIndex);
15570             }else if(!isSelected){
15571                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15572                 view.focusRow(rowIndex);
15573             }
15574         }
15575         this.fireEvent("afterselectionchange", this);
15576     },
15577     // private
15578     handleDragableRowClick :  function(grid, rowIndex, e) 
15579     {
15580         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15581             this.selectRow(rowIndex, false);
15582             grid.view.focusRow(rowIndex);
15583              this.fireEvent("afterselectionchange", this);
15584         }
15585     },
15586     
15587     /**
15588      * Selects multiple rows.
15589      * @param {Array} rows Array of the indexes of the row to select
15590      * @param {Boolean} keepExisting (optional) True to keep existing selections
15591      */
15592     selectRows : function(rows, keepExisting){
15593         if(!keepExisting){
15594             this.clearSelections();
15595         }
15596         for(var i = 0, len = rows.length; i < len; i++){
15597             this.selectRow(rows[i], true);
15598         }
15599     },
15600
15601     /**
15602      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15603      * @param {Number} startRow The index of the first row in the range
15604      * @param {Number} endRow The index of the last row in the range
15605      * @param {Boolean} keepExisting (optional) True to retain existing selections
15606      */
15607     selectRange : function(startRow, endRow, keepExisting){
15608         if(this.locked) return;
15609         if(!keepExisting){
15610             this.clearSelections();
15611         }
15612         if(startRow <= endRow){
15613             for(var i = startRow; i <= endRow; i++){
15614                 this.selectRow(i, true);
15615             }
15616         }else{
15617             for(var i = startRow; i >= endRow; i--){
15618                 this.selectRow(i, true);
15619             }
15620         }
15621     },
15622
15623     /**
15624      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15625      * @param {Number} startRow The index of the first row in the range
15626      * @param {Number} endRow The index of the last row in the range
15627      */
15628     deselectRange : function(startRow, endRow, preventViewNotify){
15629         if(this.locked) return;
15630         for(var i = startRow; i <= endRow; i++){
15631             this.deselectRow(i, preventViewNotify);
15632         }
15633     },
15634
15635     /**
15636      * Selects a row.
15637      * @param {Number} row The index of the row to select
15638      * @param {Boolean} keepExisting (optional) True to keep existing selections
15639      */
15640     selectRow : function(index, keepExisting, preventViewNotify){
15641         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15642         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15643             if(!keepExisting || this.singleSelect){
15644                 this.clearSelections();
15645             }
15646             var r = this.grid.dataSource.getAt(index);
15647             this.selections.add(r);
15648             this.last = this.lastActive = index;
15649             if(!preventViewNotify){
15650                 this.grid.getView().onRowSelect(index);
15651             }
15652             this.fireEvent("rowselect", this, index, r);
15653             this.fireEvent("selectionchange", this);
15654         }
15655     },
15656
15657     /**
15658      * Deselects a row.
15659      * @param {Number} row The index of the row to deselect
15660      */
15661     deselectRow : function(index, preventViewNotify){
15662         if(this.locked) return;
15663         if(this.last == index){
15664             this.last = false;
15665         }
15666         if(this.lastActive == index){
15667             this.lastActive = false;
15668         }
15669         var r = this.grid.dataSource.getAt(index);
15670         this.selections.remove(r);
15671         if(!preventViewNotify){
15672             this.grid.getView().onRowDeselect(index);
15673         }
15674         this.fireEvent("rowdeselect", this, index);
15675         this.fireEvent("selectionchange", this);
15676     },
15677
15678     // private
15679     restoreLast : function(){
15680         if(this._last){
15681             this.last = this._last;
15682         }
15683     },
15684
15685     // private
15686     acceptsNav : function(row, col, cm){
15687         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15688     },
15689
15690     // private
15691     onEditorKey : function(field, e){
15692         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15693         if(k == e.TAB){
15694             e.stopEvent();
15695             ed.completeEdit();
15696             if(e.shiftKey){
15697                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15698             }else{
15699                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15700             }
15701         }else if(k == e.ENTER && !e.ctrlKey){
15702             e.stopEvent();
15703             ed.completeEdit();
15704             if(e.shiftKey){
15705                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15706             }else{
15707                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15708             }
15709         }else if(k == e.ESC){
15710             ed.cancelEdit();
15711         }
15712         if(newCell){
15713             g.startEditing(newCell[0], newCell[1]);
15714         }
15715     }
15716 });/*
15717  * - LGPL
15718  *
15719  * element
15720  * 
15721  */
15722
15723 /**
15724  * @class Roo.bootstrap.MessageBar
15725  * @extends Roo.bootstrap.Component
15726  * Bootstrap MessageBar class
15727  * @cfg {String} html contents of the MessageBar
15728  * @cfg {String} weight (info | success | warning | danger) default info
15729  * @cfg {String} beforeClass insert the bar before the given class
15730  * @cfg {Boolean} closable (true | false) default false
15731  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15732  * 
15733  * @constructor
15734  * Create a new Element
15735  * @param {Object} config The config object
15736  */
15737
15738 Roo.bootstrap.MessageBar = function(config){
15739     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15740 };
15741
15742 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15743     
15744     html: '',
15745     weight: 'info',
15746     closable: false,
15747     fixed: false,
15748     beforeClass: 'bootstrap-sticky-wrap',
15749     
15750     getAutoCreate : function(){
15751         
15752         var cfg = {
15753             tag: 'div',
15754             cls: 'alert alert-dismissable alert-' + this.weight,
15755             cn: [
15756                 {
15757                     tag: 'span',
15758                     cls: 'message',
15759                     html: this.html || ''
15760                 }
15761             ]
15762         }
15763         
15764         if(this.fixed){
15765             cfg.cls += ' alert-messages-fixed';
15766         }
15767         
15768         if(this.closable){
15769             cfg.cn.push({
15770                 tag: 'button',
15771                 cls: 'close',
15772                 html: 'x'
15773             });
15774         }
15775         
15776         return cfg;
15777     },
15778     
15779     onRender : function(ct, position)
15780     {
15781         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15782         
15783         if(!this.el){
15784             var cfg = Roo.apply({},  this.getAutoCreate());
15785             cfg.id = Roo.id();
15786             
15787             if (this.cls) {
15788                 cfg.cls += ' ' + this.cls;
15789             }
15790             if (this.style) {
15791                 cfg.style = this.style;
15792             }
15793             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15794             
15795             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15796         }
15797         
15798         this.el.select('>button.close').on('click', this.hide, this);
15799         
15800     },
15801     
15802     show : function()
15803     {
15804         if (!this.rendered) {
15805             this.render();
15806         }
15807         
15808         this.el.show();
15809         
15810         this.fireEvent('show', this);
15811         
15812     },
15813     
15814     hide : function()
15815     {
15816         if (!this.rendered) {
15817             this.render();
15818         }
15819         
15820         this.el.hide();
15821         
15822         this.fireEvent('hide', this);
15823     },
15824     
15825     update : function()
15826     {
15827 //        var e = this.el.dom.firstChild;
15828 //        
15829 //        if(this.closable){
15830 //            e = e.nextSibling;
15831 //        }
15832 //        
15833 //        e.data = this.html || '';
15834
15835         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15836     }
15837    
15838 });
15839
15840  
15841
15842