roojs-core.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 config = cm.config[i];
3025                     var value = '';
3026                     var id = Roo.id();
3027                     
3028                     if(typeof(renderer) !== 'undefined'){
3029                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3030                     }
3031                     
3032                     if(typeof(value) === 'object'){
3033                         renders.push({
3034                             id : id,
3035                             cfg : value 
3036                         })
3037                     }
3038                     
3039                     var td = {
3040                         tag: 'td',
3041                         id: id,
3042                         html: (typeof(value) === 'object') ? '' : value
3043                     };
3044                     
3045                     if(typeof(config.width) != 'undefined'){
3046                         td.width = config.width;
3047                     }
3048                     
3049                     row.cn.push(td);
3050                    
3051                 }
3052                 
3053                 tbody.createChild(row);
3054                 
3055             });
3056         }
3057         
3058         
3059         if(renders.length){
3060             var _this = this;
3061             Roo.each(renders, function(r){
3062                 _this.renderColumn(r);
3063             })
3064         }
3065 //        
3066 //        if(this.loadMask){
3067 //            this.maskEl.hide();
3068 //        }
3069     },
3070     
3071     onBeforeLoad : function()
3072     {
3073         Roo.log('ds onBeforeLoad');
3074         
3075         this.clear();
3076         
3077 //        if(this.loadMask){
3078 //            this.maskEl.show();
3079 //        }
3080     },
3081     
3082     clear : function()
3083     {
3084         this.el.select('tbody', true).first().dom.innerHTML = '';
3085     },
3086     
3087     getSelectionModel : function(){
3088         if(!this.selModel){
3089             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3090         }
3091         return this.selModel;
3092     },
3093     
3094     renderColumn : function(r)
3095     {
3096         var _this = this;
3097         r.cfg.render(Roo.get(r.id));
3098         
3099         if(r.cfg.cn){
3100             Roo.each(r.cfg.cn, function(c){
3101                 var child = {
3102                     id: r.id,
3103                     cfg: c
3104                 }
3105                 _this.renderColumn(child);
3106             })
3107         }
3108     }
3109    
3110 });
3111
3112  
3113
3114  /*
3115  * - LGPL
3116  *
3117  * table cell
3118  * 
3119  */
3120
3121 /**
3122  * @class Roo.bootstrap.TableCell
3123  * @extends Roo.bootstrap.Component
3124  * Bootstrap TableCell class
3125  * @cfg {String} html cell contain text
3126  * @cfg {String} cls cell class
3127  * @cfg {String} tag cell tag (td|th) default td
3128  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3129  * @cfg {String} align Aligns the content in a cell
3130  * @cfg {String} axis Categorizes cells
3131  * @cfg {String} bgcolor Specifies the background color of a cell
3132  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3133  * @cfg {Number} colspan Specifies the number of columns a cell should span
3134  * @cfg {String} headers Specifies one or more header cells a cell is related to
3135  * @cfg {Number} height Sets the height of a cell
3136  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3137  * @cfg {Number} rowspan Sets the number of rows a cell should span
3138  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3139  * @cfg {String} valign Vertical aligns the content in a cell
3140  * @cfg {Number} width Specifies the width of a cell
3141  * 
3142  * @constructor
3143  * Create a new TableCell
3144  * @param {Object} config The config object
3145  */
3146
3147 Roo.bootstrap.TableCell = function(config){
3148     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3149 };
3150
3151 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3152     
3153     html: false,
3154     cls: false,
3155     tag: false,
3156     abbr: false,
3157     align: false,
3158     axis: false,
3159     bgcolor: false,
3160     charoff: false,
3161     colspan: false,
3162     headers: false,
3163     height: false,
3164     nowrap: false,
3165     rowspan: false,
3166     scope: false,
3167     valign: false,
3168     width: false,
3169     
3170     
3171     getAutoCreate : function(){
3172         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3173         
3174         cfg = {
3175             tag: 'td'
3176         }
3177         
3178         if(this.tag){
3179             cfg.tag = this.tag;
3180         }
3181         
3182         if (this.html) {
3183             cfg.html=this.html
3184         }
3185         if (this.cls) {
3186             cfg.cls=this.cls
3187         }
3188         if (this.abbr) {
3189             cfg.abbr=this.abbr
3190         }
3191         if (this.align) {
3192             cfg.align=this.align
3193         }
3194         if (this.axis) {
3195             cfg.axis=this.axis
3196         }
3197         if (this.bgcolor) {
3198             cfg.bgcolor=this.bgcolor
3199         }
3200         if (this.charoff) {
3201             cfg.charoff=this.charoff
3202         }
3203         if (this.colspan) {
3204             cfg.colspan=this.colspan
3205         }
3206         if (this.headers) {
3207             cfg.headers=this.headers
3208         }
3209         if (this.height) {
3210             cfg.height=this.height
3211         }
3212         if (this.nowrap) {
3213             cfg.nowrap=this.nowrap
3214         }
3215         if (this.rowspan) {
3216             cfg.rowspan=this.rowspan
3217         }
3218         if (this.scope) {
3219             cfg.scope=this.scope
3220         }
3221         if (this.valign) {
3222             cfg.valign=this.valign
3223         }
3224         if (this.width) {
3225             cfg.width=this.width
3226         }
3227         
3228         
3229         return cfg;
3230     }
3231    
3232 });
3233
3234  
3235
3236  /*
3237  * - LGPL
3238  *
3239  * table row
3240  * 
3241  */
3242
3243 /**
3244  * @class Roo.bootstrap.TableRow
3245  * @extends Roo.bootstrap.Component
3246  * Bootstrap TableRow class
3247  * @cfg {String} cls row class
3248  * @cfg {String} align Aligns the content in a table row
3249  * @cfg {String} bgcolor Specifies a background color for a table row
3250  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3251  * @cfg {String} valign Vertical aligns the content in a table row
3252  * 
3253  * @constructor
3254  * Create a new TableRow
3255  * @param {Object} config The config object
3256  */
3257
3258 Roo.bootstrap.TableRow = function(config){
3259     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3260 };
3261
3262 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3263     
3264     cls: false,
3265     align: false,
3266     bgcolor: false,
3267     charoff: false,
3268     valign: false,
3269     
3270     getAutoCreate : function(){
3271         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3272         
3273         cfg = {
3274             tag: 'tr'
3275         }
3276             
3277         if(this.cls){
3278             cfg.cls = this.cls;
3279         }
3280         if(this.align){
3281             cfg.align = this.align;
3282         }
3283         if(this.bgcolor){
3284             cfg.bgcolor = this.bgcolor;
3285         }
3286         if(this.charoff){
3287             cfg.charoff = this.charoff;
3288         }
3289         if(this.valign){
3290             cfg.valign = this.valign;
3291         }
3292         
3293         return cfg;
3294     }
3295    
3296 });
3297
3298  
3299
3300  /*
3301  * - LGPL
3302  *
3303  * table body
3304  * 
3305  */
3306
3307 /**
3308  * @class Roo.bootstrap.TableBody
3309  * @extends Roo.bootstrap.Component
3310  * Bootstrap TableBody class
3311  * @cfg {String} cls element class
3312  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3313  * @cfg {String} align Aligns the content inside the element
3314  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3315  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3316  * 
3317  * @constructor
3318  * Create a new TableBody
3319  * @param {Object} config The config object
3320  */
3321
3322 Roo.bootstrap.TableBody = function(config){
3323     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3324 };
3325
3326 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3327     
3328     cls: false,
3329     tag: false,
3330     align: false,
3331     charoff: false,
3332     valign: false,
3333     
3334     getAutoCreate : function(){
3335         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3336         
3337         cfg = {
3338             tag: 'tbody'
3339         }
3340             
3341         if (this.cls) {
3342             cfg.cls=this.cls
3343         }
3344         if(this.tag){
3345             cfg.tag = this.tag;
3346         }
3347         
3348         if(this.align){
3349             cfg.align = this.align;
3350         }
3351         if(this.charoff){
3352             cfg.charoff = this.charoff;
3353         }
3354         if(this.valign){
3355             cfg.valign = this.valign;
3356         }
3357         
3358         return cfg;
3359     }
3360     
3361     
3362 //    initEvents : function()
3363 //    {
3364 //        
3365 //        if(!this.store){
3366 //            return;
3367 //        }
3368 //        
3369 //        this.store = Roo.factory(this.store, Roo.data);
3370 //        this.store.on('load', this.onLoad, this);
3371 //        
3372 //        this.store.load();
3373 //        
3374 //    },
3375 //    
3376 //    onLoad: function () 
3377 //    {   
3378 //        this.fireEvent('load', this);
3379 //    }
3380 //    
3381 //   
3382 });
3383
3384  
3385
3386  /*
3387  * Based on:
3388  * Ext JS Library 1.1.1
3389  * Copyright(c) 2006-2007, Ext JS, LLC.
3390  *
3391  * Originally Released Under LGPL - original licence link has changed is not relivant.
3392  *
3393  * Fork - LGPL
3394  * <script type="text/javascript">
3395  */
3396
3397 // as we use this in bootstrap.
3398 Roo.namespace('Roo.form');
3399  /**
3400  * @class Roo.form.Action
3401  * Internal Class used to handle form actions
3402  * @constructor
3403  * @param {Roo.form.BasicForm} el The form element or its id
3404  * @param {Object} config Configuration options
3405  */
3406
3407  
3408  
3409 // define the action interface
3410 Roo.form.Action = function(form, options){
3411     this.form = form;
3412     this.options = options || {};
3413 };
3414 /**
3415  * Client Validation Failed
3416  * @const 
3417  */
3418 Roo.form.Action.CLIENT_INVALID = 'client';
3419 /**
3420  * Server Validation Failed
3421  * @const 
3422  */
3423 Roo.form.Action.SERVER_INVALID = 'server';
3424  /**
3425  * Connect to Server Failed
3426  * @const 
3427  */
3428 Roo.form.Action.CONNECT_FAILURE = 'connect';
3429 /**
3430  * Reading Data from Server Failed
3431  * @const 
3432  */
3433 Roo.form.Action.LOAD_FAILURE = 'load';
3434
3435 Roo.form.Action.prototype = {
3436     type : 'default',
3437     failureType : undefined,
3438     response : undefined,
3439     result : undefined,
3440
3441     // interface method
3442     run : function(options){
3443
3444     },
3445
3446     // interface method
3447     success : function(response){
3448
3449     },
3450
3451     // interface method
3452     handleResponse : function(response){
3453
3454     },
3455
3456     // default connection failure
3457     failure : function(response){
3458         
3459         this.response = response;
3460         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3461         this.form.afterAction(this, false);
3462     },
3463
3464     processResponse : function(response){
3465         this.response = response;
3466         if(!response.responseText){
3467             return true;
3468         }
3469         this.result = this.handleResponse(response);
3470         return this.result;
3471     },
3472
3473     // utility functions used internally
3474     getUrl : function(appendParams){
3475         var url = this.options.url || this.form.url || this.form.el.dom.action;
3476         if(appendParams){
3477             var p = this.getParams();
3478             if(p){
3479                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3480             }
3481         }
3482         return url;
3483     },
3484
3485     getMethod : function(){
3486         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3487     },
3488
3489     getParams : function(){
3490         var bp = this.form.baseParams;
3491         var p = this.options.params;
3492         if(p){
3493             if(typeof p == "object"){
3494                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3495             }else if(typeof p == 'string' && bp){
3496                 p += '&' + Roo.urlEncode(bp);
3497             }
3498         }else if(bp){
3499             p = Roo.urlEncode(bp);
3500         }
3501         return p;
3502     },
3503
3504     createCallback : function(){
3505         return {
3506             success: this.success,
3507             failure: this.failure,
3508             scope: this,
3509             timeout: (this.form.timeout*1000),
3510             upload: this.form.fileUpload ? this.success : undefined
3511         };
3512     }
3513 };
3514
3515 Roo.form.Action.Submit = function(form, options){
3516     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3517 };
3518
3519 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3520     type : 'submit',
3521
3522     haveProgress : false,
3523     uploadComplete : false,
3524     
3525     // uploadProgress indicator.
3526     uploadProgress : function()
3527     {
3528         if (!this.form.progressUrl) {
3529             return;
3530         }
3531         
3532         if (!this.haveProgress) {
3533             Roo.MessageBox.progress("Uploading", "Uploading");
3534         }
3535         if (this.uploadComplete) {
3536            Roo.MessageBox.hide();
3537            return;
3538         }
3539         
3540         this.haveProgress = true;
3541    
3542         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3543         
3544         var c = new Roo.data.Connection();
3545         c.request({
3546             url : this.form.progressUrl,
3547             params: {
3548                 id : uid
3549             },
3550             method: 'GET',
3551             success : function(req){
3552                //console.log(data);
3553                 var rdata = false;
3554                 var edata;
3555                 try  {
3556                    rdata = Roo.decode(req.responseText)
3557                 } catch (e) {
3558                     Roo.log("Invalid data from server..");
3559                     Roo.log(edata);
3560                     return;
3561                 }
3562                 if (!rdata || !rdata.success) {
3563                     Roo.log(rdata);
3564                     Roo.MessageBox.alert(Roo.encode(rdata));
3565                     return;
3566                 }
3567                 var data = rdata.data;
3568                 
3569                 if (this.uploadComplete) {
3570                    Roo.MessageBox.hide();
3571                    return;
3572                 }
3573                    
3574                 if (data){
3575                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3576                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3577                     );
3578                 }
3579                 this.uploadProgress.defer(2000,this);
3580             },
3581        
3582             failure: function(data) {
3583                 Roo.log('progress url failed ');
3584                 Roo.log(data);
3585             },
3586             scope : this
3587         });
3588            
3589     },
3590     
3591     
3592     run : function()
3593     {
3594         // run get Values on the form, so it syncs any secondary forms.
3595         this.form.getValues();
3596         
3597         var o = this.options;
3598         var method = this.getMethod();
3599         var isPost = method == 'POST';
3600         if(o.clientValidation === false || this.form.isValid()){
3601             
3602             if (this.form.progressUrl) {
3603                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3604                     (new Date() * 1) + '' + Math.random());
3605                     
3606             } 
3607             
3608             
3609             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3610                 form:this.form.el.dom,
3611                 url:this.getUrl(!isPost),
3612                 method: method,
3613                 params:isPost ? this.getParams() : null,
3614                 isUpload: this.form.fileUpload
3615             }));
3616             
3617             this.uploadProgress();
3618
3619         }else if (o.clientValidation !== false){ // client validation failed
3620             this.failureType = Roo.form.Action.CLIENT_INVALID;
3621             this.form.afterAction(this, false);
3622         }
3623     },
3624
3625     success : function(response)
3626     {
3627         this.uploadComplete= true;
3628         if (this.haveProgress) {
3629             Roo.MessageBox.hide();
3630         }
3631         
3632         
3633         var result = this.processResponse(response);
3634         if(result === true || result.success){
3635             this.form.afterAction(this, true);
3636             return;
3637         }
3638         if(result.errors){
3639             this.form.markInvalid(result.errors);
3640             this.failureType = Roo.form.Action.SERVER_INVALID;
3641         }
3642         this.form.afterAction(this, false);
3643     },
3644     failure : function(response)
3645     {
3646         this.uploadComplete= true;
3647         if (this.haveProgress) {
3648             Roo.MessageBox.hide();
3649         }
3650         
3651         this.response = response;
3652         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3653         this.form.afterAction(this, false);
3654     },
3655     
3656     handleResponse : function(response){
3657         if(this.form.errorReader){
3658             var rs = this.form.errorReader.read(response);
3659             var errors = [];
3660             if(rs.records){
3661                 for(var i = 0, len = rs.records.length; i < len; i++) {
3662                     var r = rs.records[i];
3663                     errors[i] = r.data;
3664                 }
3665             }
3666             if(errors.length < 1){
3667                 errors = null;
3668             }
3669             return {
3670                 success : rs.success,
3671                 errors : errors
3672             };
3673         }
3674         var ret = false;
3675         try {
3676             ret = Roo.decode(response.responseText);
3677         } catch (e) {
3678             ret = {
3679                 success: false,
3680                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3681                 errors : []
3682             };
3683         }
3684         return ret;
3685         
3686     }
3687 });
3688
3689
3690 Roo.form.Action.Load = function(form, options){
3691     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3692     this.reader = this.form.reader;
3693 };
3694
3695 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3696     type : 'load',
3697
3698     run : function(){
3699         
3700         Roo.Ajax.request(Roo.apply(
3701                 this.createCallback(), {
3702                     method:this.getMethod(),
3703                     url:this.getUrl(false),
3704                     params:this.getParams()
3705         }));
3706     },
3707
3708     success : function(response){
3709         
3710         var result = this.processResponse(response);
3711         if(result === true || !result.success || !result.data){
3712             this.failureType = Roo.form.Action.LOAD_FAILURE;
3713             this.form.afterAction(this, false);
3714             return;
3715         }
3716         this.form.clearInvalid();
3717         this.form.setValues(result.data);
3718         this.form.afterAction(this, true);
3719     },
3720
3721     handleResponse : function(response){
3722         if(this.form.reader){
3723             var rs = this.form.reader.read(response);
3724             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3725             return {
3726                 success : rs.success,
3727                 data : data
3728             };
3729         }
3730         return Roo.decode(response.responseText);
3731     }
3732 });
3733
3734 Roo.form.Action.ACTION_TYPES = {
3735     'load' : Roo.form.Action.Load,
3736     'submit' : Roo.form.Action.Submit
3737 };/*
3738  * - LGPL
3739  *
3740  * form
3741  * 
3742  */
3743
3744 /**
3745  * @class Roo.bootstrap.Form
3746  * @extends Roo.bootstrap.Component
3747  * Bootstrap Form class
3748  * @cfg {String} method  GET | POST (default POST)
3749  * @cfg {String} labelAlign top | left (default top)
3750   * @cfg {String} align left  | right - for navbars
3751
3752  * 
3753  * @constructor
3754  * Create a new Form
3755  * @param {Object} config The config object
3756  */
3757
3758
3759 Roo.bootstrap.Form = function(config){
3760     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3761     this.addEvents({
3762         /**
3763          * @event clientvalidation
3764          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3765          * @param {Form} this
3766          * @param {Boolean} valid true if the form has passed client-side validation
3767          */
3768         clientvalidation: true,
3769         /**
3770          * @event beforeaction
3771          * Fires before any action is performed. Return false to cancel the action.
3772          * @param {Form} this
3773          * @param {Action} action The action to be performed
3774          */
3775         beforeaction: true,
3776         /**
3777          * @event actionfailed
3778          * Fires when an action fails.
3779          * @param {Form} this
3780          * @param {Action} action The action that failed
3781          */
3782         actionfailed : true,
3783         /**
3784          * @event actioncomplete
3785          * Fires when an action is completed.
3786          * @param {Form} this
3787          * @param {Action} action The action that completed
3788          */
3789         actioncomplete : true
3790     });
3791     
3792 };
3793
3794 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3795       
3796      /**
3797      * @cfg {String} method
3798      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3799      */
3800     method : 'POST',
3801     /**
3802      * @cfg {String} url
3803      * The URL to use for form actions if one isn't supplied in the action options.
3804      */
3805     /**
3806      * @cfg {Boolean} fileUpload
3807      * Set to true if this form is a file upload.
3808      */
3809      
3810     /**
3811      * @cfg {Object} baseParams
3812      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3813      */
3814       
3815     /**
3816      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3817      */
3818     timeout: 30,
3819     /**
3820      * @cfg {Sting} align (left|right) for navbar forms
3821      */
3822     align : 'left',
3823
3824     // private
3825     activeAction : null,
3826  
3827     /**
3828      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3829      * element by passing it or its id or mask the form itself by passing in true.
3830      * @type Mixed
3831      */
3832     waitMsgTarget : false,
3833     
3834      
3835     
3836     /**
3837      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3838      * element by passing it or its id or mask the form itself by passing in true.
3839      * @type Mixed
3840      */
3841     
3842     getAutoCreate : function(){
3843         
3844         var cfg = {
3845             tag: 'form',
3846             method : this.method || 'POST',
3847             id : this.id || Roo.id(),
3848             cls : ''
3849         }
3850         if (this.parent().xtype.match(/^Nav/)) {
3851             cfg.cls = 'navbar-form navbar-' + this.align;
3852             
3853         }
3854         
3855         if (this.labelAlign == 'left' ) {
3856             cfg.cls += ' form-horizontal';
3857         }
3858         
3859         
3860         return cfg;
3861     },
3862     initEvents : function()
3863     {
3864         this.el.on('submit', this.onSubmit, this);
3865         
3866         
3867     },
3868     // private
3869     onSubmit : function(e){
3870         e.stopEvent();
3871     },
3872     
3873      /**
3874      * Returns true if client-side validation on the form is successful.
3875      * @return Boolean
3876      */
3877     isValid : function(){
3878         var items = this.getItems();
3879         var valid = true;
3880         items.each(function(f){
3881            if(!f.validate()){
3882                valid = false;
3883                
3884            }
3885         });
3886         return valid;
3887     },
3888     /**
3889      * Returns true if any fields in this form have changed since their original load.
3890      * @return Boolean
3891      */
3892     isDirty : function(){
3893         var dirty = false;
3894         var items = this.getItems();
3895         items.each(function(f){
3896            if(f.isDirty()){
3897                dirty = true;
3898                return false;
3899            }
3900            return true;
3901         });
3902         return dirty;
3903     },
3904      /**
3905      * Performs a predefined action (submit or load) or custom actions you define on this form.
3906      * @param {String} actionName The name of the action type
3907      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3908      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3909      * accept other config options):
3910      * <pre>
3911 Property          Type             Description
3912 ----------------  ---------------  ----------------------------------------------------------------------------------
3913 url               String           The url for the action (defaults to the form's url)
3914 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3915 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3916 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3917                                    validate the form on the client (defaults to false)
3918      * </pre>
3919      * @return {BasicForm} this
3920      */
3921     doAction : function(action, options){
3922         if(typeof action == 'string'){
3923             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3924         }
3925         if(this.fireEvent('beforeaction', this, action) !== false){
3926             this.beforeAction(action);
3927             action.run.defer(100, action);
3928         }
3929         return this;
3930     },
3931     
3932     // private
3933     beforeAction : function(action){
3934         var o = action.options;
3935         
3936         // not really supported yet.. ??
3937         
3938         //if(this.waitMsgTarget === true){
3939             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3940         //}else if(this.waitMsgTarget){
3941         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3942         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3943         //}else {
3944         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3945        // }
3946          
3947     },
3948
3949     // private
3950     afterAction : function(action, success){
3951         this.activeAction = null;
3952         var o = action.options;
3953         
3954         //if(this.waitMsgTarget === true){
3955             this.el.unmask();
3956         //}else if(this.waitMsgTarget){
3957         //    this.waitMsgTarget.unmask();
3958         //}else{
3959         //    Roo.MessageBox.updateProgress(1);
3960         //    Roo.MessageBox.hide();
3961        // }
3962         // 
3963         if(success){
3964             if(o.reset){
3965                 this.reset();
3966             }
3967             Roo.callback(o.success, o.scope, [this, action]);
3968             this.fireEvent('actioncomplete', this, action);
3969             
3970         }else{
3971             
3972             // failure condition..
3973             // we have a scenario where updates need confirming.
3974             // eg. if a locking scenario exists..
3975             // we look for { errors : { needs_confirm : true }} in the response.
3976             if (
3977                 (typeof(action.result) != 'undefined')  &&
3978                 (typeof(action.result.errors) != 'undefined')  &&
3979                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3980            ){
3981                 var _t = this;
3982                 Roo.log("not supported yet");
3983                  /*
3984                 
3985                 Roo.MessageBox.confirm(
3986                     "Change requires confirmation",
3987                     action.result.errorMsg,
3988                     function(r) {
3989                         if (r != 'yes') {
3990                             return;
3991                         }
3992                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
3993                     }
3994                     
3995                 );
3996                 */
3997                 
3998                 
3999                 return;
4000             }
4001             
4002             Roo.callback(o.failure, o.scope, [this, action]);
4003             // show an error message if no failed handler is set..
4004             if (!this.hasListener('actionfailed')) {
4005                 Roo.log("need to add dialog support");
4006                 /*
4007                 Roo.MessageBox.alert("Error",
4008                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4009                         action.result.errorMsg :
4010                         "Saving Failed, please check your entries or try again"
4011                 );
4012                 */
4013             }
4014             
4015             this.fireEvent('actionfailed', this, action);
4016         }
4017         
4018     },
4019     /**
4020      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4021      * @param {String} id The value to search for
4022      * @return Field
4023      */
4024     findField : function(id){
4025         var items = this.getItems();
4026         var field = items.get(id);
4027         if(!field){
4028              items.each(function(f){
4029                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4030                     field = f;
4031                     return false;
4032                 }
4033                 return true;
4034             });
4035         }
4036         return field || null;
4037     },
4038      /**
4039      * Mark fields in this form invalid in bulk.
4040      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4041      * @return {BasicForm} this
4042      */
4043     markInvalid : function(errors){
4044         if(errors instanceof Array){
4045             for(var i = 0, len = errors.length; i < len; i++){
4046                 var fieldError = errors[i];
4047                 var f = this.findField(fieldError.id);
4048                 if(f){
4049                     f.markInvalid(fieldError.msg);
4050                 }
4051             }
4052         }else{
4053             var field, id;
4054             for(id in errors){
4055                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4056                     field.markInvalid(errors[id]);
4057                 }
4058             }
4059         }
4060         //Roo.each(this.childForms || [], function (f) {
4061         //    f.markInvalid(errors);
4062         //});
4063         
4064         return this;
4065     },
4066
4067     /**
4068      * Set values for fields in this form in bulk.
4069      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4070      * @return {BasicForm} this
4071      */
4072     setValues : function(values){
4073         if(values instanceof Array){ // array of objects
4074             for(var i = 0, len = values.length; i < len; i++){
4075                 var v = values[i];
4076                 var f = this.findField(v.id);
4077                 if(f){
4078                     f.setValue(v.value);
4079                     if(this.trackResetOnLoad){
4080                         f.originalValue = f.getValue();
4081                     }
4082                 }
4083             }
4084         }else{ // object hash
4085             var field, id;
4086             for(id in values){
4087                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4088                     
4089                     if (field.setFromData && 
4090                         field.valueField && 
4091                         field.displayField &&
4092                         // combos' with local stores can 
4093                         // be queried via setValue()
4094                         // to set their value..
4095                         (field.store && !field.store.isLocal)
4096                         ) {
4097                         // it's a combo
4098                         var sd = { };
4099                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4100                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4101                         field.setFromData(sd);
4102                         
4103                     } else {
4104                         field.setValue(values[id]);
4105                     }
4106                     
4107                     
4108                     if(this.trackResetOnLoad){
4109                         field.originalValue = field.getValue();
4110                     }
4111                 }
4112             }
4113         }
4114          
4115         //Roo.each(this.childForms || [], function (f) {
4116         //    f.setValues(values);
4117         //});
4118                 
4119         return this;
4120     },
4121
4122     /**
4123      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4124      * they are returned as an array.
4125      * @param {Boolean} asString
4126      * @return {Object}
4127      */
4128     getValues : function(asString){
4129         //if (this.childForms) {
4130             // copy values from the child forms
4131         //    Roo.each(this.childForms, function (f) {
4132         //        this.setValues(f.getValues());
4133         //    }, this);
4134         //}
4135         
4136         
4137         
4138         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4139         if(asString === true){
4140             return fs;
4141         }
4142         return Roo.urlDecode(fs);
4143     },
4144     
4145     /**
4146      * Returns the fields in this form as an object with key/value pairs. 
4147      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4148      * @return {Object}
4149      */
4150     getFieldValues : function(with_hidden)
4151     {
4152         var items = this.getItems();
4153         var ret = {};
4154         items.each(function(f){
4155             if (!f.getName()) {
4156                 return;
4157             }
4158             var v = f.getValue();
4159             if (f.inputType =='radio') {
4160                 if (typeof(ret[f.getName()]) == 'undefined') {
4161                     ret[f.getName()] = ''; // empty..
4162                 }
4163                 
4164                 if (!f.el.dom.checked) {
4165                     return;
4166                     
4167                 }
4168                 v = f.el.dom.value;
4169                 
4170             }
4171             
4172             // not sure if this supported any more..
4173             if ((typeof(v) == 'object') && f.getRawValue) {
4174                 v = f.getRawValue() ; // dates..
4175             }
4176             // combo boxes where name != hiddenName...
4177             if (f.name != f.getName()) {
4178                 ret[f.name] = f.getRawValue();
4179             }
4180             ret[f.getName()] = v;
4181         });
4182         
4183         return ret;
4184     },
4185
4186     /**
4187      * Clears all invalid messages in this form.
4188      * @return {BasicForm} this
4189      */
4190     clearInvalid : function(){
4191         var items = this.getItems();
4192         
4193         items.each(function(f){
4194            f.clearInvalid();
4195         });
4196         
4197         
4198         
4199         return this;
4200     },
4201
4202     /**
4203      * Resets this form.
4204      * @return {BasicForm} this
4205      */
4206     reset : function(){
4207         var items = this.getItems();
4208         items.each(function(f){
4209             f.reset();
4210         });
4211         
4212         Roo.each(this.childForms || [], function (f) {
4213             f.reset();
4214         });
4215        
4216         
4217         return this;
4218     },
4219     getItems : function()
4220     {
4221         var r=new Roo.util.MixedCollection(false, function(o){
4222             return o.id || (o.id = Roo.id());
4223         });
4224         var iter = function(el) {
4225             if (el.inputEl) {
4226                 r.add(el);
4227             }
4228             if (!el.items) {
4229                 return;
4230             }
4231             Roo.each(el.items,function(e) {
4232                 iter(e);
4233             });
4234             
4235             
4236         };
4237         iter(this);
4238         return r;
4239         
4240         
4241         
4242         
4243     }
4244     
4245 });
4246
4247  
4248 /*
4249  * Based on:
4250  * Ext JS Library 1.1.1
4251  * Copyright(c) 2006-2007, Ext JS, LLC.
4252  *
4253  * Originally Released Under LGPL - original licence link has changed is not relivant.
4254  *
4255  * Fork - LGPL
4256  * <script type="text/javascript">
4257  */
4258 /**
4259  * @class Roo.form.VTypes
4260  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4261  * @singleton
4262  */
4263 Roo.form.VTypes = function(){
4264     // closure these in so they are only created once.
4265     var alpha = /^[a-zA-Z_]+$/;
4266     var alphanum = /^[a-zA-Z0-9_]+$/;
4267     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4268     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4269
4270     // All these messages and functions are configurable
4271     return {
4272         /**
4273          * The function used to validate email addresses
4274          * @param {String} value The email address
4275          */
4276         'email' : function(v){
4277             return email.test(v);
4278         },
4279         /**
4280          * The error text to display when the email validation function returns false
4281          * @type String
4282          */
4283         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4284         /**
4285          * The keystroke filter mask to be applied on email input
4286          * @type RegExp
4287          */
4288         'emailMask' : /[a-z0-9_\.\-@]/i,
4289
4290         /**
4291          * The function used to validate URLs
4292          * @param {String} value The URL
4293          */
4294         'url' : function(v){
4295             return url.test(v);
4296         },
4297         /**
4298          * The error text to display when the url validation function returns false
4299          * @type String
4300          */
4301         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4302         
4303         /**
4304          * The function used to validate alpha values
4305          * @param {String} value The value
4306          */
4307         'alpha' : function(v){
4308             return alpha.test(v);
4309         },
4310         /**
4311          * The error text to display when the alpha validation function returns false
4312          * @type String
4313          */
4314         'alphaText' : 'This field should only contain letters and _',
4315         /**
4316          * The keystroke filter mask to be applied on alpha input
4317          * @type RegExp
4318          */
4319         'alphaMask' : /[a-z_]/i,
4320
4321         /**
4322          * The function used to validate alphanumeric values
4323          * @param {String} value The value
4324          */
4325         'alphanum' : function(v){
4326             return alphanum.test(v);
4327         },
4328         /**
4329          * The error text to display when the alphanumeric validation function returns false
4330          * @type String
4331          */
4332         'alphanumText' : 'This field should only contain letters, numbers and _',
4333         /**
4334          * The keystroke filter mask to be applied on alphanumeric input
4335          * @type RegExp
4336          */
4337         'alphanumMask' : /[a-z0-9_]/i
4338     };
4339 }();/*
4340  * - LGPL
4341  *
4342  * Input
4343  * 
4344  */
4345
4346 /**
4347  * @class Roo.bootstrap.Input
4348  * @extends Roo.bootstrap.Component
4349  * Bootstrap Input class
4350  * @cfg {Boolean} disabled is it disabled
4351  * @cfg {String} fieldLabel - the label associated
4352  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4353  * @cfg {String} name name of the input
4354  * @cfg {string} fieldLabel - the label associated
4355  * @cfg {string}  inputType - input / file submit ...
4356  * @cfg {string} placeholder - placeholder to put in text.
4357  * @cfg {string}  before - input group add on before
4358  * @cfg {string} after - input group add on after
4359  * @cfg {string} size - (lg|sm) or leave empty..
4360  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4361  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4362  * @cfg {Number} md colspan out of 12 for computer-sized screens
4363  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4364  * @cfg {string} value default value of the input
4365  * @cfg {Number} labelWidth set the width of label (0-12)
4366  * @cfg {String} labelAlign (top|left)
4367  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4368  * 
4369  * 
4370  * @constructor
4371  * Create a new Input
4372  * @param {Object} config The config object
4373  */
4374
4375 Roo.bootstrap.Input = function(config){
4376     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4377    
4378         this.addEvents({
4379             /**
4380              * @event focus
4381              * Fires when this field receives input focus.
4382              * @param {Roo.form.Field} this
4383              */
4384             focus : true,
4385             /**
4386              * @event blur
4387              * Fires when this field loses input focus.
4388              * @param {Roo.form.Field} this
4389              */
4390             blur : true,
4391             /**
4392              * @event specialkey
4393              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4394              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4395              * @param {Roo.form.Field} this
4396              * @param {Roo.EventObject} e The event object
4397              */
4398             specialkey : true,
4399             /**
4400              * @event change
4401              * Fires just before the field blurs if the field value has changed.
4402              * @param {Roo.form.Field} this
4403              * @param {Mixed} newValue The new value
4404              * @param {Mixed} oldValue The original value
4405              */
4406             change : true,
4407             /**
4408              * @event invalid
4409              * Fires after the field has been marked as invalid.
4410              * @param {Roo.form.Field} this
4411              * @param {String} msg The validation message
4412              */
4413             invalid : true,
4414             /**
4415              * @event valid
4416              * Fires after the field has been validated with no errors.
4417              * @param {Roo.form.Field} this
4418              */
4419             valid : true,
4420              /**
4421              * @event keyup
4422              * Fires after the key up
4423              * @param {Roo.form.Field} this
4424              * @param {Roo.EventObject}  e The event Object
4425              */
4426             keyup : true
4427         });
4428 };
4429
4430 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4431      /**
4432      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4433       automatic validation (defaults to "keyup").
4434      */
4435     validationEvent : "keyup",
4436      /**
4437      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4438      */
4439     validateOnBlur : true,
4440     /**
4441      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4442      */
4443     validationDelay : 250,
4444      /**
4445      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4446      */
4447     focusClass : "x-form-focus",  // not needed???
4448     
4449        
4450     /**
4451      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4452      */
4453     invalidClass : "has-error",
4454     
4455     /**
4456      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4457      */
4458     selectOnFocus : false,
4459     
4460      /**
4461      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4462      */
4463     maskRe : null,
4464        /**
4465      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4466      */
4467     vtype : null,
4468     
4469       /**
4470      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4471      */
4472     disableKeyFilter : false,
4473     
4474        /**
4475      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4476      */
4477     disabled : false,
4478      /**
4479      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4480      */
4481     allowBlank : true,
4482     /**
4483      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4484      */
4485     blankText : "This field is required",
4486     
4487      /**
4488      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4489      */
4490     minLength : 0,
4491     /**
4492      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4493      */
4494     maxLength : Number.MAX_VALUE,
4495     /**
4496      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4497      */
4498     minLengthText : "The minimum length for this field is {0}",
4499     /**
4500      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4501      */
4502     maxLengthText : "The maximum length for this field is {0}",
4503   
4504     
4505     /**
4506      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4507      * If available, this function will be called only after the basic validators all return true, and will be passed the
4508      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4509      */
4510     validator : null,
4511     /**
4512      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4513      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4514      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4515      */
4516     regex : null,
4517     /**
4518      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4519      */
4520     regexText : "",
4521     
4522     
4523     
4524     fieldLabel : '',
4525     inputType : 'text',
4526     
4527     name : false,
4528     placeholder: false,
4529     before : false,
4530     after : false,
4531     size : false,
4532     // private
4533     hasFocus : false,
4534     preventMark: false,
4535     isFormField : true,
4536     value : '',
4537     labelWidth : 2,
4538     labelAlign : false,
4539     readOnly : false,
4540     
4541     parentLabelAlign : function()
4542     {
4543         var parent = this;
4544         while (parent.parent()) {
4545             parent = parent.parent();
4546             if (typeof(parent.labelAlign) !='undefined') {
4547                 return parent.labelAlign;
4548             }
4549         }
4550         return 'left';
4551         
4552     },
4553     
4554     getAutoCreate : function(){
4555         
4556         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4557         
4558         var id = Roo.id();
4559         
4560         var cfg = {};
4561         
4562         if(this.inputType != 'hidden'){
4563             cfg.cls = 'form-group' //input-group
4564         }
4565         
4566         var input =  {
4567             tag: 'input',
4568             id : id,
4569             type : this.inputType,
4570             value : this.value,
4571             cls : 'form-control',
4572             placeholder : this.placeholder || ''
4573             
4574         };
4575         
4576         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4577             input.maxLength = this.maxLength;
4578         }
4579         
4580         if (this.disabled) {
4581             input.disabled=true;
4582         }
4583         
4584         if (this.readOnly) {
4585             input.readonly=true;
4586         }
4587         
4588         if (this.name) {
4589             input.name = this.name;
4590         }
4591         if (this.size) {
4592             input.cls += ' input-' + this.size;
4593         }
4594         var settings=this;
4595         ['xs','sm','md','lg'].map(function(size){
4596             if (settings[size]) {
4597                 cfg.cls += ' col-' + size + '-' + settings[size];
4598             }
4599         });
4600         
4601         var inputblock = input;
4602         
4603         if (this.before || this.after) {
4604             
4605             inputblock = {
4606                 cls : 'input-group',
4607                 cn :  [] 
4608             };
4609             if (this.before) {
4610                 inputblock.cn.push({
4611                     tag :'span',
4612                     cls : 'input-group-addon',
4613                     html : this.before
4614                 });
4615             }
4616             inputblock.cn.push(input);
4617             if (this.after) {
4618                 inputblock.cn.push({
4619                     tag :'span',
4620                     cls : 'input-group-addon',
4621                     html : this.after
4622                 });
4623             }
4624             
4625         };
4626         
4627         if (align ==='left' && this.fieldLabel.length) {
4628                 Roo.log("left and has label");
4629                 cfg.cn = [
4630                     
4631                     {
4632                         tag: 'label',
4633                         'for' :  id,
4634                         cls : 'control-label col-sm-' + this.labelWidth,
4635                         html : this.fieldLabel
4636                         
4637                     },
4638                     {
4639                         cls : "col-sm-" + (12 - this.labelWidth), 
4640                         cn: [
4641                             inputblock
4642                         ]
4643                     }
4644                     
4645                 ];
4646         } else if ( this.fieldLabel.length) {
4647                 Roo.log(" label");
4648                  cfg.cn = [
4649                    
4650                     {
4651                         tag: 'label',
4652                         //cls : 'input-group-addon',
4653                         html : this.fieldLabel
4654                         
4655                     },
4656                     
4657                     inputblock
4658                     
4659                 ];
4660
4661         } else {
4662             
4663                 Roo.log(" no label && no align");
4664                 cfg.cn = [
4665                     
4666                         inputblock
4667                     
4668                 ];
4669                 
4670                 
4671         };
4672         Roo.log('input-parentType: ' + this.parentType);
4673         
4674         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4675            cfg.cls += ' navbar-form';
4676            Roo.log(cfg);
4677         }
4678         
4679         return cfg;
4680         
4681     },
4682     /**
4683      * return the real input element.
4684      */
4685     inputEl: function ()
4686     {
4687         return this.el.select('input.form-control',true).first();
4688     },
4689     setDisabled : function(v)
4690     {
4691         var i  = this.inputEl().dom;
4692         if (!v) {
4693             i.removeAttribute('disabled');
4694             return;
4695             
4696         }
4697         i.setAttribute('disabled','true');
4698     },
4699     initEvents : function()
4700     {
4701         
4702         this.inputEl().on("keydown" , this.fireKey,  this);
4703         this.inputEl().on("focus", this.onFocus,  this);
4704         this.inputEl().on("blur", this.onBlur,  this);
4705         
4706         this.inputEl().relayEvent('keyup', this);
4707
4708         // reference to original value for reset
4709         this.originalValue = this.getValue();
4710         //Roo.form.TextField.superclass.initEvents.call(this);
4711         if(this.validationEvent == 'keyup'){
4712             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4713             this.inputEl().on('keyup', this.filterValidation, this);
4714         }
4715         else if(this.validationEvent !== false){
4716             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4717         }
4718         
4719         if(this.selectOnFocus){
4720             this.on("focus", this.preFocus, this);
4721             
4722         }
4723         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4724             this.inputEl().on("keypress", this.filterKeys, this);
4725         }
4726        /* if(this.grow){
4727             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4728             this.el.on("click", this.autoSize,  this);
4729         }
4730         */
4731         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4732             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4733         }
4734         
4735     },
4736     filterValidation : function(e){
4737         if(!e.isNavKeyPress()){
4738             this.validationTask.delay(this.validationDelay);
4739         }
4740     },
4741      /**
4742      * Validates the field value
4743      * @return {Boolean} True if the value is valid, else false
4744      */
4745     validate : function(){
4746         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4747         if(this.disabled || this.validateValue(this.getRawValue())){
4748             this.clearInvalid();
4749             return true;
4750         }
4751         return false;
4752     },
4753     
4754     
4755     /**
4756      * Validates a value according to the field's validation rules and marks the field as invalid
4757      * if the validation fails
4758      * @param {Mixed} value The value to validate
4759      * @return {Boolean} True if the value is valid, else false
4760      */
4761     validateValue : function(value){
4762         if(value.length < 1)  { // if it's blank
4763              if(this.allowBlank){
4764                 this.clearInvalid();
4765                 return true;
4766              }else{
4767                 this.markInvalid(this.blankText);
4768                 return false;
4769              }
4770         }
4771         if(value.length < this.minLength){
4772             this.markInvalid(String.format(this.minLengthText, this.minLength));
4773             return false;
4774         }
4775         if(value.length > this.maxLength){
4776             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4777             return false;
4778         }
4779         if(this.vtype){
4780             var vt = Roo.form.VTypes;
4781             if(!vt[this.vtype](value, this)){
4782                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4783                 return false;
4784             }
4785         }
4786         if(typeof this.validator == "function"){
4787             var msg = this.validator(value);
4788             if(msg !== true){
4789                 this.markInvalid(msg);
4790                 return false;
4791             }
4792         }
4793         if(this.regex && !this.regex.test(value)){
4794             this.markInvalid(this.regexText);
4795             return false;
4796         }
4797         return true;
4798     },
4799
4800     
4801     
4802      // private
4803     fireKey : function(e){
4804         //Roo.log('field ' + e.getKey());
4805         if(e.isNavKeyPress()){
4806             this.fireEvent("specialkey", this, e);
4807         }
4808     },
4809     focus : function (selectText){
4810         if(this.rendered){
4811             this.inputEl().focus();
4812             if(selectText === true){
4813                 this.inputEl().dom.select();
4814             }
4815         }
4816         return this;
4817     } ,
4818     
4819     onFocus : function(){
4820         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4821            // this.el.addClass(this.focusClass);
4822         }
4823         if(!this.hasFocus){
4824             this.hasFocus = true;
4825             this.startValue = this.getValue();
4826             this.fireEvent("focus", this);
4827         }
4828     },
4829     
4830     beforeBlur : Roo.emptyFn,
4831
4832     
4833     // private
4834     onBlur : function(){
4835         this.beforeBlur();
4836         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4837             //this.el.removeClass(this.focusClass);
4838         }
4839         this.hasFocus = false;
4840         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4841             this.validate();
4842         }
4843         var v = this.getValue();
4844         if(String(v) !== String(this.startValue)){
4845             this.fireEvent('change', this, v, this.startValue);
4846         }
4847         this.fireEvent("blur", this);
4848     },
4849     
4850     /**
4851      * Resets the current field value to the originally loaded value and clears any validation messages
4852      */
4853     reset : function(){
4854         this.setValue(this.originalValue);
4855         this.clearInvalid();
4856     },
4857      /**
4858      * Returns the name of the field
4859      * @return {Mixed} name The name field
4860      */
4861     getName: function(){
4862         return this.name;
4863     },
4864      /**
4865      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4866      * @return {Mixed} value The field value
4867      */
4868     getValue : function(){
4869         return this.inputEl().getValue();
4870     },
4871     /**
4872      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4873      * @return {Mixed} value The field value
4874      */
4875     getRawValue : function(){
4876         var v = this.inputEl().getValue();
4877         
4878         return v;
4879     },
4880     
4881     /**
4882      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4883      * @param {Mixed} value The value to set
4884      */
4885     setRawValue : function(v){
4886         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4887     },
4888     
4889     selectText : function(start, end){
4890         var v = this.getRawValue();
4891         if(v.length > 0){
4892             start = start === undefined ? 0 : start;
4893             end = end === undefined ? v.length : end;
4894             var d = this.inputEl().dom;
4895             if(d.setSelectionRange){
4896                 d.setSelectionRange(start, end);
4897             }else if(d.createTextRange){
4898                 var range = d.createTextRange();
4899                 range.moveStart("character", start);
4900                 range.moveEnd("character", v.length-end);
4901                 range.select();
4902             }
4903         }
4904     },
4905     
4906     /**
4907      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4908      * @param {Mixed} value The value to set
4909      */
4910     setValue : function(v){
4911         this.value = v;
4912         if(this.rendered){
4913             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4914             this.validate();
4915         }
4916     },
4917     
4918     /*
4919     processValue : function(value){
4920         if(this.stripCharsRe){
4921             var newValue = value.replace(this.stripCharsRe, '');
4922             if(newValue !== value){
4923                 this.setRawValue(newValue);
4924                 return newValue;
4925             }
4926         }
4927         return value;
4928     },
4929   */
4930     preFocus : function(){
4931         
4932         if(this.selectOnFocus){
4933             this.inputEl().dom.select();
4934         }
4935     },
4936     filterKeys : function(e){
4937         var k = e.getKey();
4938         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4939             return;
4940         }
4941         var c = e.getCharCode(), cc = String.fromCharCode(c);
4942         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4943             return;
4944         }
4945         if(!this.maskRe.test(cc)){
4946             e.stopEvent();
4947         }
4948     },
4949      /**
4950      * Clear any invalid styles/messages for this field
4951      */
4952     clearInvalid : function(){
4953         
4954         if(!this.el || this.preventMark){ // not rendered
4955             return;
4956         }
4957         this.el.removeClass(this.invalidClass);
4958         /*
4959         switch(this.msgTarget){
4960             case 'qtip':
4961                 this.el.dom.qtip = '';
4962                 break;
4963             case 'title':
4964                 this.el.dom.title = '';
4965                 break;
4966             case 'under':
4967                 if(this.errorEl){
4968                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4969                 }
4970                 break;
4971             case 'side':
4972                 if(this.errorIcon){
4973                     this.errorIcon.dom.qtip = '';
4974                     this.errorIcon.hide();
4975                     this.un('resize', this.alignErrorIcon, this);
4976                 }
4977                 break;
4978             default:
4979                 var t = Roo.getDom(this.msgTarget);
4980                 t.innerHTML = '';
4981                 t.style.display = 'none';
4982                 break;
4983         }
4984         */
4985         this.fireEvent('valid', this);
4986     },
4987      /**
4988      * Mark this field as invalid
4989      * @param {String} msg The validation message
4990      */
4991     markInvalid : function(msg){
4992         if(!this.el  || this.preventMark){ // not rendered
4993             return;
4994         }
4995         this.el.addClass(this.invalidClass);
4996         /*
4997         msg = msg || this.invalidText;
4998         switch(this.msgTarget){
4999             case 'qtip':
5000                 this.el.dom.qtip = msg;
5001                 this.el.dom.qclass = 'x-form-invalid-tip';
5002                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5003                     Roo.QuickTips.enable();
5004                 }
5005                 break;
5006             case 'title':
5007                 this.el.dom.title = msg;
5008                 break;
5009             case 'under':
5010                 if(!this.errorEl){
5011                     var elp = this.el.findParent('.x-form-element', 5, true);
5012                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5013                     this.errorEl.setWidth(elp.getWidth(true)-20);
5014                 }
5015                 this.errorEl.update(msg);
5016                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5017                 break;
5018             case 'side':
5019                 if(!this.errorIcon){
5020                     var elp = this.el.findParent('.x-form-element', 5, true);
5021                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5022                 }
5023                 this.alignErrorIcon();
5024                 this.errorIcon.dom.qtip = msg;
5025                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5026                 this.errorIcon.show();
5027                 this.on('resize', this.alignErrorIcon, this);
5028                 break;
5029             default:
5030                 var t = Roo.getDom(this.msgTarget);
5031                 t.innerHTML = msg;
5032                 t.style.display = this.msgDisplay;
5033                 break;
5034         }
5035         */
5036         this.fireEvent('invalid', this, msg);
5037     },
5038     // private
5039     SafariOnKeyDown : function(event)
5040     {
5041         // this is a workaround for a password hang bug on chrome/ webkit.
5042         
5043         var isSelectAll = false;
5044         
5045         if(this.inputEl().dom.selectionEnd > 0){
5046             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5047         }
5048         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5049             event.preventDefault();
5050             this.setValue('');
5051             return;
5052         }
5053         
5054         if(isSelectAll){ // backspace and delete key
5055             
5056             event.preventDefault();
5057             // this is very hacky as keydown always get's upper case.
5058             //
5059             var cc = String.fromCharCode(event.getCharCode());
5060             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5061             
5062         }
5063     },
5064     adjustWidth : function(tag, w){
5065         tag = tag.toLowerCase();
5066         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5067             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5068                 if(tag == 'input'){
5069                     return w + 2;
5070                 }
5071                 if(tag == 'textarea'){
5072                     return w-2;
5073                 }
5074             }else if(Roo.isOpera){
5075                 if(tag == 'input'){
5076                     return w + 2;
5077                 }
5078                 if(tag == 'textarea'){
5079                     return w-2;
5080                 }
5081             }
5082         }
5083         return w;
5084     }
5085     
5086 });
5087
5088  
5089 /*
5090  * - LGPL
5091  *
5092  * Input
5093  * 
5094  */
5095
5096 /**
5097  * @class Roo.bootstrap.TextArea
5098  * @extends Roo.bootstrap.Input
5099  * Bootstrap TextArea class
5100  * @cfg {Number} cols Specifies the visible width of a text area
5101  * @cfg {Number} rows Specifies the visible number of lines in a text area
5102  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5103  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5104  * @cfg {string} html text
5105  * 
5106  * @constructor
5107  * Create a new TextArea
5108  * @param {Object} config The config object
5109  */
5110
5111 Roo.bootstrap.TextArea = function(config){
5112     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5113    
5114 };
5115
5116 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5117      
5118     cols : false,
5119     rows : 5,
5120     readOnly : false,
5121     warp : 'soft',
5122     resize : false,
5123     value: false,
5124     html: false,
5125     
5126     getAutoCreate : function(){
5127         
5128         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5129         
5130         var id = Roo.id();
5131         
5132         var cfg = {};
5133         
5134         var input =  {
5135             tag: 'textarea',
5136             id : id,
5137             warp : this.warp,
5138             rows : this.rows,
5139             value : this.value || '',
5140             html: this.html || '',
5141             cls : 'form-control',
5142             placeholder : this.placeholder || '' 
5143             
5144         };
5145         
5146         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5147             input.maxLength = this.maxLength;
5148         }
5149         
5150         if(this.resize){
5151             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5152         }
5153         
5154         if(this.cols){
5155             input.cols = this.cols;
5156         }
5157         
5158         if (this.readOnly) {
5159             input.readonly = true;
5160         }
5161         
5162         if (this.name) {
5163             input.name = this.name;
5164         }
5165         
5166         if (this.size) {
5167             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5168         }
5169         
5170         var settings=this;
5171         ['xs','sm','md','lg'].map(function(size){
5172             if (settings[size]) {
5173                 cfg.cls += ' col-' + size + '-' + settings[size];
5174             }
5175         });
5176         
5177         var inputblock = input;
5178         
5179         if (this.before || this.after) {
5180             
5181             inputblock = {
5182                 cls : 'input-group',
5183                 cn :  [] 
5184             };
5185             if (this.before) {
5186                 inputblock.cn.push({
5187                     tag :'span',
5188                     cls : 'input-group-addon',
5189                     html : this.before
5190                 });
5191             }
5192             inputblock.cn.push(input);
5193             if (this.after) {
5194                 inputblock.cn.push({
5195                     tag :'span',
5196                     cls : 'input-group-addon',
5197                     html : this.after
5198                 });
5199             }
5200             
5201         }
5202         
5203         if (align ==='left' && this.fieldLabel.length) {
5204                 Roo.log("left and has label");
5205                 cfg.cn = [
5206                     
5207                     {
5208                         tag: 'label',
5209                         'for' :  id,
5210                         cls : 'control-label col-sm-' + this.labelWidth,
5211                         html : this.fieldLabel
5212                         
5213                     },
5214                     {
5215                         cls : "col-sm-" + (12 - this.labelWidth), 
5216                         cn: [
5217                             inputblock
5218                         ]
5219                     }
5220                     
5221                 ];
5222         } else if ( this.fieldLabel.length) {
5223                 Roo.log(" label");
5224                  cfg.cn = [
5225                    
5226                     {
5227                         tag: 'label',
5228                         //cls : 'input-group-addon',
5229                         html : this.fieldLabel
5230                         
5231                     },
5232                     
5233                     inputblock
5234                     
5235                 ];
5236
5237         } else {
5238             
5239                    Roo.log(" no label && no align");
5240                 cfg.cn = [
5241                     
5242                         inputblock
5243                     
5244                 ];
5245                 
5246                 
5247         }
5248         
5249         if (this.disabled) {
5250             input.disabled=true;
5251         }
5252         
5253         return cfg;
5254         
5255     },
5256     /**
5257      * return the real textarea element.
5258      */
5259     inputEl: function ()
5260     {
5261         return this.el.select('textarea.form-control',true).first();
5262     }
5263 });
5264
5265  
5266 /*
5267  * - LGPL
5268  *
5269  * trigger field - base class for combo..
5270  * 
5271  */
5272  
5273 /**
5274  * @class Roo.bootstrap.TriggerField
5275  * @extends Roo.bootstrap.Input
5276  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5277  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5278  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5279  * for which you can provide a custom implementation.  For example:
5280  * <pre><code>
5281 var trigger = new Roo.bootstrap.TriggerField();
5282 trigger.onTriggerClick = myTriggerFn;
5283 trigger.applyTo('my-field');
5284 </code></pre>
5285  *
5286  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5287  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5288  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5289  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5290  * @constructor
5291  * Create a new TriggerField.
5292  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5293  * to the base TextField)
5294  */
5295 Roo.bootstrap.TriggerField = function(config){
5296     this.mimicing = false;
5297     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5298 };
5299
5300 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5301     /**
5302      * @cfg {String} triggerClass A CSS class to apply to the trigger
5303      */
5304      /**
5305      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5306      */
5307     hideTrigger:false,
5308
5309     /** @cfg {Boolean} grow @hide */
5310     /** @cfg {Number} growMin @hide */
5311     /** @cfg {Number} growMax @hide */
5312
5313     /**
5314      * @hide 
5315      * @method
5316      */
5317     autoSize: Roo.emptyFn,
5318     // private
5319     monitorTab : true,
5320     // private
5321     deferHeight : true,
5322
5323     
5324     actionMode : 'wrap',
5325     
5326     
5327     
5328     getAutoCreate : function(){
5329        
5330         var parent = this.parent();
5331         
5332         var align = this.parentLabelAlign();
5333         
5334         var id = Roo.id();
5335         
5336         var cfg = {
5337             cls: 'form-group' //input-group
5338         };
5339         
5340         
5341         var input =  {
5342             tag: 'input',
5343             id : id,
5344             type : this.inputType,
5345             cls : 'form-control',
5346             autocomplete: 'off',
5347             placeholder : this.placeholder || '' 
5348             
5349         };
5350         if (this.name) {
5351             input.name = this.name;
5352         }
5353         if (this.size) {
5354             input.cls += ' input-' + this.size;
5355         }
5356         
5357         if (this.disabled) {
5358             input.disabled=true;
5359         }
5360         
5361         var inputblock = input;
5362         
5363         if (this.before || this.after) {
5364             
5365             inputblock = {
5366                 cls : 'input-group',
5367                 cn :  [] 
5368             };
5369             if (this.before) {
5370                 inputblock.cn.push({
5371                     tag :'span',
5372                     cls : 'input-group-addon',
5373                     html : this.before
5374                 });
5375             }
5376             inputblock.cn.push(input);
5377             if (this.after) {
5378                 inputblock.cn.push({
5379                     tag :'span',
5380                     cls : 'input-group-addon',
5381                     html : this.after
5382                 });
5383             }
5384             
5385         };
5386         
5387         var box = {
5388             tag: 'div',
5389             cn: [
5390                 {
5391                     tag: 'input',
5392                     type : 'hidden',
5393                     cls: 'form-hidden-field'
5394                 },
5395                 inputblock
5396             ]
5397             
5398         };
5399         
5400         if(this.multiple){
5401             Roo.log('multiple');
5402             
5403             box = {
5404                 tag: 'div',
5405                 cn: [
5406                     {
5407                         tag: 'input',
5408                         type : 'hidden',
5409                         cls: 'form-hidden-field'
5410                     },
5411                     {
5412                         tag: 'ul',
5413                         cls: 'select2-choices',
5414                         cn:[
5415                             {
5416                                 tag: 'li',
5417                                 cls: 'select2-search-field',
5418                                 cn: [
5419
5420                                     inputblock
5421                                 ]
5422                             }
5423                         ]
5424                     }
5425                 ]
5426             }
5427         };
5428         
5429         var combobox = {
5430             cls: 'select2-container input-group',
5431             cn: [
5432                 box,
5433                 {
5434                     tag: 'ul',
5435                     cls: 'typeahead typeahead-long dropdown-menu',
5436                     style: 'display:none'
5437                 }
5438             ]
5439         };
5440         
5441         if(!this.multiple){
5442             combobox.cn.push({
5443                 tag :'span',
5444                 cls : 'input-group-addon btn dropdown-toggle',
5445                 cn : [
5446                     {
5447                         tag: 'span',
5448                         cls: 'caret'
5449                     },
5450                     {
5451                         tag: 'span',
5452                         cls: 'combobox-clear',
5453                         cn  : [
5454                             {
5455                                 tag : 'i',
5456                                 cls: 'icon-remove'
5457                             }
5458                         ]
5459                     }
5460                 ]
5461
5462             })
5463         }
5464         
5465         if(this.multiple){
5466             combobox.cls += ' select2-container-multi';
5467         }
5468         
5469         if (align ==='left' && this.fieldLabel.length) {
5470             
5471                 Roo.log("left and has label");
5472                 cfg.cn = [
5473                     
5474                     {
5475                         tag: 'label',
5476                         'for' :  id,
5477                         cls : 'control-label col-sm-' + this.labelWidth,
5478                         html : this.fieldLabel
5479                         
5480                     },
5481                     {
5482                         cls : "col-sm-" + (12 - this.labelWidth), 
5483                         cn: [
5484                             combobox
5485                         ]
5486                     }
5487                     
5488                 ];
5489         } else if ( this.fieldLabel.length) {
5490                 Roo.log(" label");
5491                  cfg.cn = [
5492                    
5493                     {
5494                         tag: 'label',
5495                         //cls : 'input-group-addon',
5496                         html : this.fieldLabel
5497                         
5498                     },
5499                     
5500                     combobox
5501                     
5502                 ];
5503
5504         } else {
5505             
5506                 Roo.log(" no label && no align");
5507                 cfg = combobox
5508                      
5509                 
5510         }
5511          
5512         var settings=this;
5513         ['xs','sm','md','lg'].map(function(size){
5514             if (settings[size]) {
5515                 cfg.cls += ' col-' + size + '-' + settings[size];
5516             }
5517         });
5518         
5519         return cfg;
5520         
5521     },
5522     
5523     
5524     
5525     // private
5526     onResize : function(w, h){
5527 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5528 //        if(typeof w == 'number'){
5529 //            var x = w - this.trigger.getWidth();
5530 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5531 //            this.trigger.setStyle('left', x+'px');
5532 //        }
5533     },
5534
5535     // private
5536     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5537
5538     // private
5539     getResizeEl : function(){
5540         return this.inputEl();
5541     },
5542
5543     // private
5544     getPositionEl : function(){
5545         return this.inputEl();
5546     },
5547
5548     // private
5549     alignErrorIcon : function(){
5550         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5551     },
5552
5553     // private
5554     initEvents : function(){
5555         
5556         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5557         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5558         if(!this.multiple){
5559             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5560             if(this.hideTrigger){
5561                 this.trigger.setDisplayed(false);
5562             }
5563             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5564         }
5565         
5566         if(this.multiple){
5567             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5568         }
5569         
5570         //this.trigger.addClassOnOver('x-form-trigger-over');
5571         //this.trigger.addClassOnClick('x-form-trigger-click');
5572         
5573         //if(!this.width){
5574         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5575         //}
5576     },
5577
5578     // private
5579     initTrigger : function(){
5580        
5581     },
5582
5583     // private
5584     onDestroy : function(){
5585         if(this.trigger){
5586             this.trigger.removeAllListeners();
5587           //  this.trigger.remove();
5588         }
5589         //if(this.wrap){
5590         //    this.wrap.remove();
5591         //}
5592         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5593     },
5594
5595     // private
5596     onFocus : function(){
5597         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5598         /*
5599         if(!this.mimicing){
5600             this.wrap.addClass('x-trigger-wrap-focus');
5601             this.mimicing = true;
5602             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5603             if(this.monitorTab){
5604                 this.el.on("keydown", this.checkTab, this);
5605             }
5606         }
5607         */
5608     },
5609
5610     // private
5611     checkTab : function(e){
5612         if(e.getKey() == e.TAB){
5613             this.triggerBlur();
5614         }
5615     },
5616
5617     // private
5618     onBlur : function(){
5619         // do nothing
5620     },
5621
5622     // private
5623     mimicBlur : function(e, t){
5624         /*
5625         if(!this.wrap.contains(t) && this.validateBlur()){
5626             this.triggerBlur();
5627         }
5628         */
5629     },
5630
5631     // private
5632     triggerBlur : function(){
5633         this.mimicing = false;
5634         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5635         if(this.monitorTab){
5636             this.el.un("keydown", this.checkTab, this);
5637         }
5638         //this.wrap.removeClass('x-trigger-wrap-focus');
5639         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5640     },
5641
5642     // private
5643     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5644     validateBlur : function(e, t){
5645         return true;
5646     },
5647
5648     // private
5649     onDisable : function(){
5650         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5651         //if(this.wrap){
5652         //    this.wrap.addClass('x-item-disabled');
5653         //}
5654     },
5655
5656     // private
5657     onEnable : function(){
5658         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5659         //if(this.wrap){
5660         //    this.el.removeClass('x-item-disabled');
5661         //}
5662     },
5663
5664     // private
5665     onShow : function(){
5666         var ae = this.getActionEl();
5667         
5668         if(ae){
5669             ae.dom.style.display = '';
5670             ae.dom.style.visibility = 'visible';
5671         }
5672     },
5673
5674     // private
5675     
5676     onHide : function(){
5677         var ae = this.getActionEl();
5678         ae.dom.style.display = 'none';
5679     },
5680
5681     /**
5682      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5683      * by an implementing function.
5684      * @method
5685      * @param {EventObject} e
5686      */
5687     onTriggerClick : Roo.emptyFn
5688 });
5689  /*
5690  * Based on:
5691  * Ext JS Library 1.1.1
5692  * Copyright(c) 2006-2007, Ext JS, LLC.
5693  *
5694  * Originally Released Under LGPL - original licence link has changed is not relivant.
5695  *
5696  * Fork - LGPL
5697  * <script type="text/javascript">
5698  */
5699
5700
5701 /**
5702  * @class Roo.data.SortTypes
5703  * @singleton
5704  * Defines the default sorting (casting?) comparison functions used when sorting data.
5705  */
5706 Roo.data.SortTypes = {
5707     /**
5708      * Default sort that does nothing
5709      * @param {Mixed} s The value being converted
5710      * @return {Mixed} The comparison value
5711      */
5712     none : function(s){
5713         return s;
5714     },
5715     
5716     /**
5717      * The regular expression used to strip tags
5718      * @type {RegExp}
5719      * @property
5720      */
5721     stripTagsRE : /<\/?[^>]+>/gi,
5722     
5723     /**
5724      * Strips all HTML tags to sort on text only
5725      * @param {Mixed} s The value being converted
5726      * @return {String} The comparison value
5727      */
5728     asText : function(s){
5729         return String(s).replace(this.stripTagsRE, "");
5730     },
5731     
5732     /**
5733      * Strips all HTML tags to sort on text only - Case insensitive
5734      * @param {Mixed} s The value being converted
5735      * @return {String} The comparison value
5736      */
5737     asUCText : function(s){
5738         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5739     },
5740     
5741     /**
5742      * Case insensitive string
5743      * @param {Mixed} s The value being converted
5744      * @return {String} The comparison value
5745      */
5746     asUCString : function(s) {
5747         return String(s).toUpperCase();
5748     },
5749     
5750     /**
5751      * Date sorting
5752      * @param {Mixed} s The value being converted
5753      * @return {Number} The comparison value
5754      */
5755     asDate : function(s) {
5756         if(!s){
5757             return 0;
5758         }
5759         if(s instanceof Date){
5760             return s.getTime();
5761         }
5762         return Date.parse(String(s));
5763     },
5764     
5765     /**
5766      * Float sorting
5767      * @param {Mixed} s The value being converted
5768      * @return {Float} The comparison value
5769      */
5770     asFloat : function(s) {
5771         var val = parseFloat(String(s).replace(/,/g, ""));
5772         if(isNaN(val)) val = 0;
5773         return val;
5774     },
5775     
5776     /**
5777      * Integer sorting
5778      * @param {Mixed} s The value being converted
5779      * @return {Number} The comparison value
5780      */
5781     asInt : function(s) {
5782         var val = parseInt(String(s).replace(/,/g, ""));
5783         if(isNaN(val)) val = 0;
5784         return val;
5785     }
5786 };/*
5787  * Based on:
5788  * Ext JS Library 1.1.1
5789  * Copyright(c) 2006-2007, Ext JS, LLC.
5790  *
5791  * Originally Released Under LGPL - original licence link has changed is not relivant.
5792  *
5793  * Fork - LGPL
5794  * <script type="text/javascript">
5795  */
5796
5797 /**
5798 * @class Roo.data.Record
5799  * Instances of this class encapsulate both record <em>definition</em> information, and record
5800  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5801  * to access Records cached in an {@link Roo.data.Store} object.<br>
5802  * <p>
5803  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5804  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5805  * objects.<br>
5806  * <p>
5807  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5808  * @constructor
5809  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5810  * {@link #create}. The parameters are the same.
5811  * @param {Array} data An associative Array of data values keyed by the field name.
5812  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5813  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5814  * not specified an integer id is generated.
5815  */
5816 Roo.data.Record = function(data, id){
5817     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5818     this.data = data;
5819 };
5820
5821 /**
5822  * Generate a constructor for a specific record layout.
5823  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5824  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5825  * Each field definition object may contain the following properties: <ul>
5826  * <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,
5827  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5828  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5829  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5830  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5831  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5832  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5833  * this may be omitted.</p></li>
5834  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5835  * <ul><li>auto (Default, implies no conversion)</li>
5836  * <li>string</li>
5837  * <li>int</li>
5838  * <li>float</li>
5839  * <li>boolean</li>
5840  * <li>date</li></ul></p></li>
5841  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5842  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5843  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5844  * by the Reader into an object that will be stored in the Record. It is passed the
5845  * following parameters:<ul>
5846  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5847  * </ul></p></li>
5848  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5849  * </ul>
5850  * <br>usage:<br><pre><code>
5851 var TopicRecord = Roo.data.Record.create(
5852     {name: 'title', mapping: 'topic_title'},
5853     {name: 'author', mapping: 'username'},
5854     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5855     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5856     {name: 'lastPoster', mapping: 'user2'},
5857     {name: 'excerpt', mapping: 'post_text'}
5858 );
5859
5860 var myNewRecord = new TopicRecord({
5861     title: 'Do my job please',
5862     author: 'noobie',
5863     totalPosts: 1,
5864     lastPost: new Date(),
5865     lastPoster: 'Animal',
5866     excerpt: 'No way dude!'
5867 });
5868 myStore.add(myNewRecord);
5869 </code></pre>
5870  * @method create
5871  * @static
5872  */
5873 Roo.data.Record.create = function(o){
5874     var f = function(){
5875         f.superclass.constructor.apply(this, arguments);
5876     };
5877     Roo.extend(f, Roo.data.Record);
5878     var p = f.prototype;
5879     p.fields = new Roo.util.MixedCollection(false, function(field){
5880         return field.name;
5881     });
5882     for(var i = 0, len = o.length; i < len; i++){
5883         p.fields.add(new Roo.data.Field(o[i]));
5884     }
5885     f.getField = function(name){
5886         return p.fields.get(name);  
5887     };
5888     return f;
5889 };
5890
5891 Roo.data.Record.AUTO_ID = 1000;
5892 Roo.data.Record.EDIT = 'edit';
5893 Roo.data.Record.REJECT = 'reject';
5894 Roo.data.Record.COMMIT = 'commit';
5895
5896 Roo.data.Record.prototype = {
5897     /**
5898      * Readonly flag - true if this record has been modified.
5899      * @type Boolean
5900      */
5901     dirty : false,
5902     editing : false,
5903     error: null,
5904     modified: null,
5905
5906     // private
5907     join : function(store){
5908         this.store = store;
5909     },
5910
5911     /**
5912      * Set the named field to the specified value.
5913      * @param {String} name The name of the field to set.
5914      * @param {Object} value The value to set the field to.
5915      */
5916     set : function(name, value){
5917         if(this.data[name] == value){
5918             return;
5919         }
5920         this.dirty = true;
5921         if(!this.modified){
5922             this.modified = {};
5923         }
5924         if(typeof this.modified[name] == 'undefined'){
5925             this.modified[name] = this.data[name];
5926         }
5927         this.data[name] = value;
5928         if(!this.editing && this.store){
5929             this.store.afterEdit(this);
5930         }       
5931     },
5932
5933     /**
5934      * Get the value of the named field.
5935      * @param {String} name The name of the field to get the value of.
5936      * @return {Object} The value of the field.
5937      */
5938     get : function(name){
5939         return this.data[name]; 
5940     },
5941
5942     // private
5943     beginEdit : function(){
5944         this.editing = true;
5945         this.modified = {}; 
5946     },
5947
5948     // private
5949     cancelEdit : function(){
5950         this.editing = false;
5951         delete this.modified;
5952     },
5953
5954     // private
5955     endEdit : function(){
5956         this.editing = false;
5957         if(this.dirty && this.store){
5958             this.store.afterEdit(this);
5959         }
5960     },
5961
5962     /**
5963      * Usually called by the {@link Roo.data.Store} which owns the Record.
5964      * Rejects all changes made to the Record since either creation, or the last commit operation.
5965      * Modified fields are reverted to their original values.
5966      * <p>
5967      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5968      * of reject operations.
5969      */
5970     reject : function(){
5971         var m = this.modified;
5972         for(var n in m){
5973             if(typeof m[n] != "function"){
5974                 this.data[n] = m[n];
5975             }
5976         }
5977         this.dirty = false;
5978         delete this.modified;
5979         this.editing = false;
5980         if(this.store){
5981             this.store.afterReject(this);
5982         }
5983     },
5984
5985     /**
5986      * Usually called by the {@link Roo.data.Store} which owns the Record.
5987      * Commits all changes made to the Record since either creation, or the last commit operation.
5988      * <p>
5989      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5990      * of commit operations.
5991      */
5992     commit : function(){
5993         this.dirty = false;
5994         delete this.modified;
5995         this.editing = false;
5996         if(this.store){
5997             this.store.afterCommit(this);
5998         }
5999     },
6000
6001     // private
6002     hasError : function(){
6003         return this.error != null;
6004     },
6005
6006     // private
6007     clearError : function(){
6008         this.error = null;
6009     },
6010
6011     /**
6012      * Creates a copy of this record.
6013      * @param {String} id (optional) A new record id if you don't want to use this record's id
6014      * @return {Record}
6015      */
6016     copy : function(newId) {
6017         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6018     }
6019 };/*
6020  * Based on:
6021  * Ext JS Library 1.1.1
6022  * Copyright(c) 2006-2007, Ext JS, LLC.
6023  *
6024  * Originally Released Under LGPL - original licence link has changed is not relivant.
6025  *
6026  * Fork - LGPL
6027  * <script type="text/javascript">
6028  */
6029
6030
6031
6032 /**
6033  * @class Roo.data.Store
6034  * @extends Roo.util.Observable
6035  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6036  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6037  * <p>
6038  * 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
6039  * has no knowledge of the format of the data returned by the Proxy.<br>
6040  * <p>
6041  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6042  * instances from the data object. These records are cached and made available through accessor functions.
6043  * @constructor
6044  * Creates a new Store.
6045  * @param {Object} config A config object containing the objects needed for the Store to access data,
6046  * and read the data into Records.
6047  */
6048 Roo.data.Store = function(config){
6049     this.data = new Roo.util.MixedCollection(false);
6050     this.data.getKey = function(o){
6051         return o.id;
6052     };
6053     this.baseParams = {};
6054     // private
6055     this.paramNames = {
6056         "start" : "start",
6057         "limit" : "limit",
6058         "sort" : "sort",
6059         "dir" : "dir",
6060         "multisort" : "_multisort"
6061     };
6062
6063     if(config && config.data){
6064         this.inlineData = config.data;
6065         delete config.data;
6066     }
6067
6068     Roo.apply(this, config);
6069     
6070     if(this.reader){ // reader passed
6071         this.reader = Roo.factory(this.reader, Roo.data);
6072         this.reader.xmodule = this.xmodule || false;
6073         if(!this.recordType){
6074             this.recordType = this.reader.recordType;
6075         }
6076         if(this.reader.onMetaChange){
6077             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6078         }
6079     }
6080
6081     if(this.recordType){
6082         this.fields = this.recordType.prototype.fields;
6083     }
6084     this.modified = [];
6085
6086     this.addEvents({
6087         /**
6088          * @event datachanged
6089          * Fires when the data cache has changed, and a widget which is using this Store
6090          * as a Record cache should refresh its view.
6091          * @param {Store} this
6092          */
6093         datachanged : true,
6094         /**
6095          * @event metachange
6096          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6097          * @param {Store} this
6098          * @param {Object} meta The JSON metadata
6099          */
6100         metachange : true,
6101         /**
6102          * @event add
6103          * Fires when Records have been added to the Store
6104          * @param {Store} this
6105          * @param {Roo.data.Record[]} records The array of Records added
6106          * @param {Number} index The index at which the record(s) were added
6107          */
6108         add : true,
6109         /**
6110          * @event remove
6111          * Fires when a Record has been removed from the Store
6112          * @param {Store} this
6113          * @param {Roo.data.Record} record The Record that was removed
6114          * @param {Number} index The index at which the record was removed
6115          */
6116         remove : true,
6117         /**
6118          * @event update
6119          * Fires when a Record has been updated
6120          * @param {Store} this
6121          * @param {Roo.data.Record} record The Record that was updated
6122          * @param {String} operation The update operation being performed.  Value may be one of:
6123          * <pre><code>
6124  Roo.data.Record.EDIT
6125  Roo.data.Record.REJECT
6126  Roo.data.Record.COMMIT
6127          * </code></pre>
6128          */
6129         update : true,
6130         /**
6131          * @event clear
6132          * Fires when the data cache has been cleared.
6133          * @param {Store} this
6134          */
6135         clear : true,
6136         /**
6137          * @event beforeload
6138          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6139          * the load action will be canceled.
6140          * @param {Store} this
6141          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6142          */
6143         beforeload : true,
6144         /**
6145          * @event beforeloadadd
6146          * Fires after a new set of Records has been loaded.
6147          * @param {Store} this
6148          * @param {Roo.data.Record[]} records The Records that were loaded
6149          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6150          */
6151         beforeloadadd : true,
6152         /**
6153          * @event load
6154          * Fires after a new set of Records has been loaded, before they are added to the store.
6155          * @param {Store} this
6156          * @param {Roo.data.Record[]} records The Records that were loaded
6157          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6158          * @params {Object} return from reader
6159          */
6160         load : true,
6161         /**
6162          * @event loadexception
6163          * Fires if an exception occurs in the Proxy during loading.
6164          * Called with the signature of the Proxy's "loadexception" event.
6165          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6166          * 
6167          * @param {Proxy} 
6168          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6169          * @param {Object} load options 
6170          * @param {Object} jsonData from your request (normally this contains the Exception)
6171          */
6172         loadexception : true
6173     });
6174     
6175     if(this.proxy){
6176         this.proxy = Roo.factory(this.proxy, Roo.data);
6177         this.proxy.xmodule = this.xmodule || false;
6178         this.relayEvents(this.proxy,  ["loadexception"]);
6179     }
6180     this.sortToggle = {};
6181     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6182
6183     Roo.data.Store.superclass.constructor.call(this);
6184
6185     if(this.inlineData){
6186         this.loadData(this.inlineData);
6187         delete this.inlineData;
6188     }
6189 };
6190
6191 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6192      /**
6193     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6194     * without a remote query - used by combo/forms at present.
6195     */
6196     
6197     /**
6198     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6199     */
6200     /**
6201     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6202     */
6203     /**
6204     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6205     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6206     */
6207     /**
6208     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6209     * on any HTTP request
6210     */
6211     /**
6212     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6213     */
6214     /**
6215     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6216     */
6217     multiSort: false,
6218     /**
6219     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6220     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6221     */
6222     remoteSort : false,
6223
6224     /**
6225     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6226      * loaded or when a record is removed. (defaults to false).
6227     */
6228     pruneModifiedRecords : false,
6229
6230     // private
6231     lastOptions : null,
6232
6233     /**
6234      * Add Records to the Store and fires the add event.
6235      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6236      */
6237     add : function(records){
6238         records = [].concat(records);
6239         for(var i = 0, len = records.length; i < len; i++){
6240             records[i].join(this);
6241         }
6242         var index = this.data.length;
6243         this.data.addAll(records);
6244         this.fireEvent("add", this, records, index);
6245     },
6246
6247     /**
6248      * Remove a Record from the Store and fires the remove event.
6249      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6250      */
6251     remove : function(record){
6252         var index = this.data.indexOf(record);
6253         this.data.removeAt(index);
6254         if(this.pruneModifiedRecords){
6255             this.modified.remove(record);
6256         }
6257         this.fireEvent("remove", this, record, index);
6258     },
6259
6260     /**
6261      * Remove all Records from the Store and fires the clear event.
6262      */
6263     removeAll : function(){
6264         this.data.clear();
6265         if(this.pruneModifiedRecords){
6266             this.modified = [];
6267         }
6268         this.fireEvent("clear", this);
6269     },
6270
6271     /**
6272      * Inserts Records to the Store at the given index and fires the add event.
6273      * @param {Number} index The start index at which to insert the passed Records.
6274      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6275      */
6276     insert : function(index, records){
6277         records = [].concat(records);
6278         for(var i = 0, len = records.length; i < len; i++){
6279             this.data.insert(index, records[i]);
6280             records[i].join(this);
6281         }
6282         this.fireEvent("add", this, records, index);
6283     },
6284
6285     /**
6286      * Get the index within the cache of the passed Record.
6287      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6288      * @return {Number} The index of the passed Record. Returns -1 if not found.
6289      */
6290     indexOf : function(record){
6291         return this.data.indexOf(record);
6292     },
6293
6294     /**
6295      * Get the index within the cache of the Record with the passed id.
6296      * @param {String} id The id of the Record to find.
6297      * @return {Number} The index of the Record. Returns -1 if not found.
6298      */
6299     indexOfId : function(id){
6300         return this.data.indexOfKey(id);
6301     },
6302
6303     /**
6304      * Get the Record with the specified id.
6305      * @param {String} id The id of the Record to find.
6306      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6307      */
6308     getById : function(id){
6309         return this.data.key(id);
6310     },
6311
6312     /**
6313      * Get the Record at the specified index.
6314      * @param {Number} index The index of the Record to find.
6315      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6316      */
6317     getAt : function(index){
6318         return this.data.itemAt(index);
6319     },
6320
6321     /**
6322      * Returns a range of Records between specified indices.
6323      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6324      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6325      * @return {Roo.data.Record[]} An array of Records
6326      */
6327     getRange : function(start, end){
6328         return this.data.getRange(start, end);
6329     },
6330
6331     // private
6332     storeOptions : function(o){
6333         o = Roo.apply({}, o);
6334         delete o.callback;
6335         delete o.scope;
6336         this.lastOptions = o;
6337     },
6338
6339     /**
6340      * Loads the Record cache from the configured Proxy using the configured Reader.
6341      * <p>
6342      * If using remote paging, then the first load call must specify the <em>start</em>
6343      * and <em>limit</em> properties in the options.params property to establish the initial
6344      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6345      * <p>
6346      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6347      * and this call will return before the new data has been loaded. Perform any post-processing
6348      * in a callback function, or in a "load" event handler.</strong>
6349      * <p>
6350      * @param {Object} options An object containing properties which control loading options:<ul>
6351      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6352      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6353      * passed the following arguments:<ul>
6354      * <li>r : Roo.data.Record[]</li>
6355      * <li>options: Options object from the load call</li>
6356      * <li>success: Boolean success indicator</li></ul></li>
6357      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6358      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6359      * </ul>
6360      */
6361     load : function(options){
6362         options = options || {};
6363         if(this.fireEvent("beforeload", this, options) !== false){
6364             this.storeOptions(options);
6365             var p = Roo.apply(options.params || {}, this.baseParams);
6366             // if meta was not loaded from remote source.. try requesting it.
6367             if (!this.reader.metaFromRemote) {
6368                 p._requestMeta = 1;
6369             }
6370             if(this.sortInfo && this.remoteSort){
6371                 var pn = this.paramNames;
6372                 p[pn["sort"]] = this.sortInfo.field;
6373                 p[pn["dir"]] = this.sortInfo.direction;
6374             }
6375             if (this.multiSort) {
6376                 var pn = this.paramNames;
6377                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6378             }
6379             
6380             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6381         }
6382     },
6383
6384     /**
6385      * Reloads the Record cache from the configured Proxy using the configured Reader and
6386      * the options from the last load operation performed.
6387      * @param {Object} options (optional) An object containing properties which may override the options
6388      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6389      * the most recently used options are reused).
6390      */
6391     reload : function(options){
6392         this.load(Roo.applyIf(options||{}, this.lastOptions));
6393     },
6394
6395     // private
6396     // Called as a callback by the Reader during a load operation.
6397     loadRecords : function(o, options, success){
6398         if(!o || success === false){
6399             if(success !== false){
6400                 this.fireEvent("load", this, [], options, o);
6401             }
6402             if(options.callback){
6403                 options.callback.call(options.scope || this, [], options, false);
6404             }
6405             return;
6406         }
6407         // if data returned failure - throw an exception.
6408         if (o.success === false) {
6409             // show a message if no listener is registered.
6410             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6411                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6412             }
6413             // loadmask wil be hooked into this..
6414             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6415             return;
6416         }
6417         var r = o.records, t = o.totalRecords || r.length;
6418         
6419         this.fireEvent("beforeloadadd", this, r, options, o);
6420         
6421         if(!options || options.add !== true){
6422             if(this.pruneModifiedRecords){
6423                 this.modified = [];
6424             }
6425             for(var i = 0, len = r.length; i < len; i++){
6426                 r[i].join(this);
6427             }
6428             if(this.snapshot){
6429                 this.data = this.snapshot;
6430                 delete this.snapshot;
6431             }
6432             this.data.clear();
6433             this.data.addAll(r);
6434             this.totalLength = t;
6435             this.applySort();
6436             this.fireEvent("datachanged", this);
6437         }else{
6438             this.totalLength = Math.max(t, this.data.length+r.length);
6439             this.add(r);
6440         }
6441         this.fireEvent("load", this, r, options, o);
6442         if(options.callback){
6443             options.callback.call(options.scope || this, r, options, true);
6444         }
6445     },
6446
6447
6448     /**
6449      * Loads data from a passed data block. A Reader which understands the format of the data
6450      * must have been configured in the constructor.
6451      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6452      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6453      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6454      */
6455     loadData : function(o, append){
6456         var r = this.reader.readRecords(o);
6457         this.loadRecords(r, {add: append}, true);
6458     },
6459
6460     /**
6461      * Gets the number of cached records.
6462      * <p>
6463      * <em>If using paging, this may not be the total size of the dataset. If the data object
6464      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6465      * the data set size</em>
6466      */
6467     getCount : function(){
6468         return this.data.length || 0;
6469     },
6470
6471     /**
6472      * Gets the total number of records in the dataset as returned by the server.
6473      * <p>
6474      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6475      * the dataset size</em>
6476      */
6477     getTotalCount : function(){
6478         return this.totalLength || 0;
6479     },
6480
6481     /**
6482      * Returns the sort state of the Store as an object with two properties:
6483      * <pre><code>
6484  field {String} The name of the field by which the Records are sorted
6485  direction {String} The sort order, "ASC" or "DESC"
6486      * </code></pre>
6487      */
6488     getSortState : function(){
6489         return this.sortInfo;
6490     },
6491
6492     // private
6493     applySort : function(){
6494         if(this.sortInfo && !this.remoteSort){
6495             var s = this.sortInfo, f = s.field;
6496             var st = this.fields.get(f).sortType;
6497             var fn = function(r1, r2){
6498                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6499                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6500             };
6501             this.data.sort(s.direction, fn);
6502             if(this.snapshot && this.snapshot != this.data){
6503                 this.snapshot.sort(s.direction, fn);
6504             }
6505         }
6506     },
6507
6508     /**
6509      * Sets the default sort column and order to be used by the next load operation.
6510      * @param {String} fieldName The name of the field to sort by.
6511      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6512      */
6513     setDefaultSort : function(field, dir){
6514         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6515     },
6516
6517     /**
6518      * Sort the Records.
6519      * If remote sorting is used, the sort is performed on the server, and the cache is
6520      * reloaded. If local sorting is used, the cache is sorted internally.
6521      * @param {String} fieldName The name of the field to sort by.
6522      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6523      */
6524     sort : function(fieldName, dir){
6525         var f = this.fields.get(fieldName);
6526         if(!dir){
6527             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6528             
6529             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6530                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6531             }else{
6532                 dir = f.sortDir;
6533             }
6534         }
6535         this.sortToggle[f.name] = dir;
6536         this.sortInfo = {field: f.name, direction: dir};
6537         if(!this.remoteSort){
6538             this.applySort();
6539             this.fireEvent("datachanged", this);
6540         }else{
6541             this.load(this.lastOptions);
6542         }
6543     },
6544
6545     /**
6546      * Calls the specified function for each of the Records in the cache.
6547      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6548      * Returning <em>false</em> aborts and exits the iteration.
6549      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6550      */
6551     each : function(fn, scope){
6552         this.data.each(fn, scope);
6553     },
6554
6555     /**
6556      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6557      * (e.g., during paging).
6558      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6559      */
6560     getModifiedRecords : function(){
6561         return this.modified;
6562     },
6563
6564     // private
6565     createFilterFn : function(property, value, anyMatch){
6566         if(!value.exec){ // not a regex
6567             value = String(value);
6568             if(value.length == 0){
6569                 return false;
6570             }
6571             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6572         }
6573         return function(r){
6574             return value.test(r.data[property]);
6575         };
6576     },
6577
6578     /**
6579      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6580      * @param {String} property A field on your records
6581      * @param {Number} start The record index to start at (defaults to 0)
6582      * @param {Number} end The last record index to include (defaults to length - 1)
6583      * @return {Number} The sum
6584      */
6585     sum : function(property, start, end){
6586         var rs = this.data.items, v = 0;
6587         start = start || 0;
6588         end = (end || end === 0) ? end : rs.length-1;
6589
6590         for(var i = start; i <= end; i++){
6591             v += (rs[i].data[property] || 0);
6592         }
6593         return v;
6594     },
6595
6596     /**
6597      * Filter the records by a specified property.
6598      * @param {String} field A field on your records
6599      * @param {String/RegExp} value Either a string that the field
6600      * should start with or a RegExp to test against the field
6601      * @param {Boolean} anyMatch True to match any part not just the beginning
6602      */
6603     filter : function(property, value, anyMatch){
6604         var fn = this.createFilterFn(property, value, anyMatch);
6605         return fn ? this.filterBy(fn) : this.clearFilter();
6606     },
6607
6608     /**
6609      * Filter by a function. The specified function will be called with each
6610      * record in this data source. If the function returns true the record is included,
6611      * otherwise it is filtered.
6612      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6613      * @param {Object} scope (optional) The scope of the function (defaults to this)
6614      */
6615     filterBy : function(fn, scope){
6616         this.snapshot = this.snapshot || this.data;
6617         this.data = this.queryBy(fn, scope||this);
6618         this.fireEvent("datachanged", this);
6619     },
6620
6621     /**
6622      * Query the records by a specified property.
6623      * @param {String} field A field on your records
6624      * @param {String/RegExp} value Either a string that the field
6625      * should start with or a RegExp to test against the field
6626      * @param {Boolean} anyMatch True to match any part not just the beginning
6627      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6628      */
6629     query : function(property, value, anyMatch){
6630         var fn = this.createFilterFn(property, value, anyMatch);
6631         return fn ? this.queryBy(fn) : this.data.clone();
6632     },
6633
6634     /**
6635      * Query by a function. The specified function will be called with each
6636      * record in this data source. If the function returns true the record is included
6637      * in the results.
6638      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6639      * @param {Object} scope (optional) The scope of the function (defaults to this)
6640       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6641      **/
6642     queryBy : function(fn, scope){
6643         var data = this.snapshot || this.data;
6644         return data.filterBy(fn, scope||this);
6645     },
6646
6647     /**
6648      * Collects unique values for a particular dataIndex from this store.
6649      * @param {String} dataIndex The property to collect
6650      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6651      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6652      * @return {Array} An array of the unique values
6653      **/
6654     collect : function(dataIndex, allowNull, bypassFilter){
6655         var d = (bypassFilter === true && this.snapshot) ?
6656                 this.snapshot.items : this.data.items;
6657         var v, sv, r = [], l = {};
6658         for(var i = 0, len = d.length; i < len; i++){
6659             v = d[i].data[dataIndex];
6660             sv = String(v);
6661             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6662                 l[sv] = true;
6663                 r[r.length] = v;
6664             }
6665         }
6666         return r;
6667     },
6668
6669     /**
6670      * Revert to a view of the Record cache with no filtering applied.
6671      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6672      */
6673     clearFilter : function(suppressEvent){
6674         if(this.snapshot && this.snapshot != this.data){
6675             this.data = this.snapshot;
6676             delete this.snapshot;
6677             if(suppressEvent !== true){
6678                 this.fireEvent("datachanged", this);
6679             }
6680         }
6681     },
6682
6683     // private
6684     afterEdit : function(record){
6685         if(this.modified.indexOf(record) == -1){
6686             this.modified.push(record);
6687         }
6688         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6689     },
6690     
6691     // private
6692     afterReject : function(record){
6693         this.modified.remove(record);
6694         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6695     },
6696
6697     // private
6698     afterCommit : function(record){
6699         this.modified.remove(record);
6700         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6701     },
6702
6703     /**
6704      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6705      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6706      */
6707     commitChanges : function(){
6708         var m = this.modified.slice(0);
6709         this.modified = [];
6710         for(var i = 0, len = m.length; i < len; i++){
6711             m[i].commit();
6712         }
6713     },
6714
6715     /**
6716      * Cancel outstanding changes on all changed records.
6717      */
6718     rejectChanges : function(){
6719         var m = this.modified.slice(0);
6720         this.modified = [];
6721         for(var i = 0, len = m.length; i < len; i++){
6722             m[i].reject();
6723         }
6724     },
6725
6726     onMetaChange : function(meta, rtype, o){
6727         this.recordType = rtype;
6728         this.fields = rtype.prototype.fields;
6729         delete this.snapshot;
6730         this.sortInfo = meta.sortInfo || this.sortInfo;
6731         this.modified = [];
6732         this.fireEvent('metachange', this, this.reader.meta);
6733     },
6734     
6735     moveIndex : function(data, type)
6736     {
6737         var index = this.indexOf(data);
6738         
6739         var newIndex = index + type;
6740         
6741         this.remove(data);
6742         
6743         this.insert(newIndex, data);
6744         
6745     }
6746 });/*
6747  * Based on:
6748  * Ext JS Library 1.1.1
6749  * Copyright(c) 2006-2007, Ext JS, LLC.
6750  *
6751  * Originally Released Under LGPL - original licence link has changed is not relivant.
6752  *
6753  * Fork - LGPL
6754  * <script type="text/javascript">
6755  */
6756
6757 /**
6758  * @class Roo.data.SimpleStore
6759  * @extends Roo.data.Store
6760  * Small helper class to make creating Stores from Array data easier.
6761  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6762  * @cfg {Array} fields An array of field definition objects, or field name strings.
6763  * @cfg {Array} data The multi-dimensional array of data
6764  * @constructor
6765  * @param {Object} config
6766  */
6767 Roo.data.SimpleStore = function(config){
6768     Roo.data.SimpleStore.superclass.constructor.call(this, {
6769         isLocal : true,
6770         reader: new Roo.data.ArrayReader({
6771                 id: config.id
6772             },
6773             Roo.data.Record.create(config.fields)
6774         ),
6775         proxy : new Roo.data.MemoryProxy(config.data)
6776     });
6777     this.load();
6778 };
6779 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6780  * Based on:
6781  * Ext JS Library 1.1.1
6782  * Copyright(c) 2006-2007, Ext JS, LLC.
6783  *
6784  * Originally Released Under LGPL - original licence link has changed is not relivant.
6785  *
6786  * Fork - LGPL
6787  * <script type="text/javascript">
6788  */
6789
6790 /**
6791 /**
6792  * @extends Roo.data.Store
6793  * @class Roo.data.JsonStore
6794  * Small helper class to make creating Stores for JSON data easier. <br/>
6795 <pre><code>
6796 var store = new Roo.data.JsonStore({
6797     url: 'get-images.php',
6798     root: 'images',
6799     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6800 });
6801 </code></pre>
6802  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6803  * JsonReader and HttpProxy (unless inline data is provided).</b>
6804  * @cfg {Array} fields An array of field definition objects, or field name strings.
6805  * @constructor
6806  * @param {Object} config
6807  */
6808 Roo.data.JsonStore = function(c){
6809     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6810         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6811         reader: new Roo.data.JsonReader(c, c.fields)
6812     }));
6813 };
6814 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6815  * Based on:
6816  * Ext JS Library 1.1.1
6817  * Copyright(c) 2006-2007, Ext JS, LLC.
6818  *
6819  * Originally Released Under LGPL - original licence link has changed is not relivant.
6820  *
6821  * Fork - LGPL
6822  * <script type="text/javascript">
6823  */
6824
6825  
6826 Roo.data.Field = function(config){
6827     if(typeof config == "string"){
6828         config = {name: config};
6829     }
6830     Roo.apply(this, config);
6831     
6832     if(!this.type){
6833         this.type = "auto";
6834     }
6835     
6836     var st = Roo.data.SortTypes;
6837     // named sortTypes are supported, here we look them up
6838     if(typeof this.sortType == "string"){
6839         this.sortType = st[this.sortType];
6840     }
6841     
6842     // set default sortType for strings and dates
6843     if(!this.sortType){
6844         switch(this.type){
6845             case "string":
6846                 this.sortType = st.asUCString;
6847                 break;
6848             case "date":
6849                 this.sortType = st.asDate;
6850                 break;
6851             default:
6852                 this.sortType = st.none;
6853         }
6854     }
6855
6856     // define once
6857     var stripRe = /[\$,%]/g;
6858
6859     // prebuilt conversion function for this field, instead of
6860     // switching every time we're reading a value
6861     if(!this.convert){
6862         var cv, dateFormat = this.dateFormat;
6863         switch(this.type){
6864             case "":
6865             case "auto":
6866             case undefined:
6867                 cv = function(v){ return v; };
6868                 break;
6869             case "string":
6870                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6871                 break;
6872             case "int":
6873                 cv = function(v){
6874                     return v !== undefined && v !== null && v !== '' ?
6875                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6876                     };
6877                 break;
6878             case "float":
6879                 cv = function(v){
6880                     return v !== undefined && v !== null && v !== '' ?
6881                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6882                     };
6883                 break;
6884             case "bool":
6885             case "boolean":
6886                 cv = function(v){ return v === true || v === "true" || v == 1; };
6887                 break;
6888             case "date":
6889                 cv = function(v){
6890                     if(!v){
6891                         return '';
6892                     }
6893                     if(v instanceof Date){
6894                         return v;
6895                     }
6896                     if(dateFormat){
6897                         if(dateFormat == "timestamp"){
6898                             return new Date(v*1000);
6899                         }
6900                         return Date.parseDate(v, dateFormat);
6901                     }
6902                     var parsed = Date.parse(v);
6903                     return parsed ? new Date(parsed) : null;
6904                 };
6905              break;
6906             
6907         }
6908         this.convert = cv;
6909     }
6910 };
6911
6912 Roo.data.Field.prototype = {
6913     dateFormat: null,
6914     defaultValue: "",
6915     mapping: null,
6916     sortType : null,
6917     sortDir : "ASC"
6918 };/*
6919  * Based on:
6920  * Ext JS Library 1.1.1
6921  * Copyright(c) 2006-2007, Ext JS, LLC.
6922  *
6923  * Originally Released Under LGPL - original licence link has changed is not relivant.
6924  *
6925  * Fork - LGPL
6926  * <script type="text/javascript">
6927  */
6928  
6929 // Base class for reading structured data from a data source.  This class is intended to be
6930 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6931
6932 /**
6933  * @class Roo.data.DataReader
6934  * Base class for reading structured data from a data source.  This class is intended to be
6935  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6936  */
6937
6938 Roo.data.DataReader = function(meta, recordType){
6939     
6940     this.meta = meta;
6941     
6942     this.recordType = recordType instanceof Array ? 
6943         Roo.data.Record.create(recordType) : recordType;
6944 };
6945
6946 Roo.data.DataReader.prototype = {
6947      /**
6948      * Create an empty record
6949      * @param {Object} data (optional) - overlay some values
6950      * @return {Roo.data.Record} record created.
6951      */
6952     newRow :  function(d) {
6953         var da =  {};
6954         this.recordType.prototype.fields.each(function(c) {
6955             switch( c.type) {
6956                 case 'int' : da[c.name] = 0; break;
6957                 case 'date' : da[c.name] = new Date(); break;
6958                 case 'float' : da[c.name] = 0.0; break;
6959                 case 'boolean' : da[c.name] = false; break;
6960                 default : da[c.name] = ""; break;
6961             }
6962             
6963         });
6964         return new this.recordType(Roo.apply(da, d));
6965     }
6966     
6967 };/*
6968  * Based on:
6969  * Ext JS Library 1.1.1
6970  * Copyright(c) 2006-2007, Ext JS, LLC.
6971  *
6972  * Originally Released Under LGPL - original licence link has changed is not relivant.
6973  *
6974  * Fork - LGPL
6975  * <script type="text/javascript">
6976  */
6977
6978 /**
6979  * @class Roo.data.DataProxy
6980  * @extends Roo.data.Observable
6981  * This class is an abstract base class for implementations which provide retrieval of
6982  * unformatted data objects.<br>
6983  * <p>
6984  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6985  * (of the appropriate type which knows how to parse the data object) to provide a block of
6986  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6987  * <p>
6988  * Custom implementations must implement the load method as described in
6989  * {@link Roo.data.HttpProxy#load}.
6990  */
6991 Roo.data.DataProxy = function(){
6992     this.addEvents({
6993         /**
6994          * @event beforeload
6995          * Fires before a network request is made to retrieve a data object.
6996          * @param {Object} This DataProxy object.
6997          * @param {Object} params The params parameter to the load function.
6998          */
6999         beforeload : true,
7000         /**
7001          * @event load
7002          * Fires before the load method's callback is called.
7003          * @param {Object} This DataProxy object.
7004          * @param {Object} o The data object.
7005          * @param {Object} arg The callback argument object passed to the load function.
7006          */
7007         load : true,
7008         /**
7009          * @event loadexception
7010          * Fires if an Exception occurs during data retrieval.
7011          * @param {Object} This DataProxy object.
7012          * @param {Object} o The data object.
7013          * @param {Object} arg The callback argument object passed to the load function.
7014          * @param {Object} e The Exception.
7015          */
7016         loadexception : true
7017     });
7018     Roo.data.DataProxy.superclass.constructor.call(this);
7019 };
7020
7021 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7022
7023     /**
7024      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7025      */
7026 /*
7027  * Based on:
7028  * Ext JS Library 1.1.1
7029  * Copyright(c) 2006-2007, Ext JS, LLC.
7030  *
7031  * Originally Released Under LGPL - original licence link has changed is not relivant.
7032  *
7033  * Fork - LGPL
7034  * <script type="text/javascript">
7035  */
7036 /**
7037  * @class Roo.data.MemoryProxy
7038  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7039  * to the Reader when its load method is called.
7040  * @constructor
7041  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7042  */
7043 Roo.data.MemoryProxy = function(data){
7044     if (data.data) {
7045         data = data.data;
7046     }
7047     Roo.data.MemoryProxy.superclass.constructor.call(this);
7048     this.data = data;
7049 };
7050
7051 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7052     /**
7053      * Load data from the requested source (in this case an in-memory
7054      * data object passed to the constructor), read the data object into
7055      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7056      * process that block using the passed callback.
7057      * @param {Object} params This parameter is not used by the MemoryProxy class.
7058      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7059      * object into a block of Roo.data.Records.
7060      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7061      * The function must be passed <ul>
7062      * <li>The Record block object</li>
7063      * <li>The "arg" argument from the load function</li>
7064      * <li>A boolean success indicator</li>
7065      * </ul>
7066      * @param {Object} scope The scope in which to call the callback
7067      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7068      */
7069     load : function(params, reader, callback, scope, arg){
7070         params = params || {};
7071         var result;
7072         try {
7073             result = reader.readRecords(this.data);
7074         }catch(e){
7075             this.fireEvent("loadexception", this, arg, null, e);
7076             callback.call(scope, null, arg, false);
7077             return;
7078         }
7079         callback.call(scope, result, arg, true);
7080     },
7081     
7082     // private
7083     update : function(params, records){
7084         
7085     }
7086 });/*
7087  * Based on:
7088  * Ext JS Library 1.1.1
7089  * Copyright(c) 2006-2007, Ext JS, LLC.
7090  *
7091  * Originally Released Under LGPL - original licence link has changed is not relivant.
7092  *
7093  * Fork - LGPL
7094  * <script type="text/javascript">
7095  */
7096 /**
7097  * @class Roo.data.HttpProxy
7098  * @extends Roo.data.DataProxy
7099  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7100  * configured to reference a certain URL.<br><br>
7101  * <p>
7102  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7103  * from which the running page was served.<br><br>
7104  * <p>
7105  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7106  * <p>
7107  * Be aware that to enable the browser to parse an XML document, the server must set
7108  * the Content-Type header in the HTTP response to "text/xml".
7109  * @constructor
7110  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7111  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7112  * will be used to make the request.
7113  */
7114 Roo.data.HttpProxy = function(conn){
7115     Roo.data.HttpProxy.superclass.constructor.call(this);
7116     // is conn a conn config or a real conn?
7117     this.conn = conn;
7118     this.useAjax = !conn || !conn.events;
7119   
7120 };
7121
7122 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7123     // thse are take from connection...
7124     
7125     /**
7126      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7127      */
7128     /**
7129      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7130      * extra parameters to each request made by this object. (defaults to undefined)
7131      */
7132     /**
7133      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7134      *  to each request made by this object. (defaults to undefined)
7135      */
7136     /**
7137      * @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)
7138      */
7139     /**
7140      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7141      */
7142      /**
7143      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7144      * @type Boolean
7145      */
7146   
7147
7148     /**
7149      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7150      * @type Boolean
7151      */
7152     /**
7153      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7154      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7155      * a finer-grained basis than the DataProxy events.
7156      */
7157     getConnection : function(){
7158         return this.useAjax ? Roo.Ajax : this.conn;
7159     },
7160
7161     /**
7162      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7163      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7164      * process that block using the passed callback.
7165      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7166      * for the request to the remote server.
7167      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7168      * object into a block of Roo.data.Records.
7169      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7170      * The function must be passed <ul>
7171      * <li>The Record block object</li>
7172      * <li>The "arg" argument from the load function</li>
7173      * <li>A boolean success indicator</li>
7174      * </ul>
7175      * @param {Object} scope The scope in which to call the callback
7176      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7177      */
7178     load : function(params, reader, callback, scope, arg){
7179         if(this.fireEvent("beforeload", this, params) !== false){
7180             var  o = {
7181                 params : params || {},
7182                 request: {
7183                     callback : callback,
7184                     scope : scope,
7185                     arg : arg
7186                 },
7187                 reader: reader,
7188                 callback : this.loadResponse,
7189                 scope: this
7190             };
7191             if(this.useAjax){
7192                 Roo.applyIf(o, this.conn);
7193                 if(this.activeRequest){
7194                     Roo.Ajax.abort(this.activeRequest);
7195                 }
7196                 this.activeRequest = Roo.Ajax.request(o);
7197             }else{
7198                 this.conn.request(o);
7199             }
7200         }else{
7201             callback.call(scope||this, null, arg, false);
7202         }
7203     },
7204
7205     // private
7206     loadResponse : function(o, success, response){
7207         delete this.activeRequest;
7208         if(!success){
7209             this.fireEvent("loadexception", this, o, response);
7210             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7211             return;
7212         }
7213         var result;
7214         try {
7215             result = o.reader.read(response);
7216         }catch(e){
7217             this.fireEvent("loadexception", this, o, response, e);
7218             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7219             return;
7220         }
7221         
7222         this.fireEvent("load", this, o, o.request.arg);
7223         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7224     },
7225
7226     // private
7227     update : function(dataSet){
7228
7229     },
7230
7231     // private
7232     updateResponse : function(dataSet){
7233
7234     }
7235 });/*
7236  * Based on:
7237  * Ext JS Library 1.1.1
7238  * Copyright(c) 2006-2007, Ext JS, LLC.
7239  *
7240  * Originally Released Under LGPL - original licence link has changed is not relivant.
7241  *
7242  * Fork - LGPL
7243  * <script type="text/javascript">
7244  */
7245
7246 /**
7247  * @class Roo.data.ScriptTagProxy
7248  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7249  * other than the originating domain of the running page.<br><br>
7250  * <p>
7251  * <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
7252  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7253  * <p>
7254  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7255  * source code that is used as the source inside a &lt;script> tag.<br><br>
7256  * <p>
7257  * In order for the browser to process the returned data, the server must wrap the data object
7258  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7259  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7260  * depending on whether the callback name was passed:
7261  * <p>
7262  * <pre><code>
7263 boolean scriptTag = false;
7264 String cb = request.getParameter("callback");
7265 if (cb != null) {
7266     scriptTag = true;
7267     response.setContentType("text/javascript");
7268 } else {
7269     response.setContentType("application/x-json");
7270 }
7271 Writer out = response.getWriter();
7272 if (scriptTag) {
7273     out.write(cb + "(");
7274 }
7275 out.print(dataBlock.toJsonString());
7276 if (scriptTag) {
7277     out.write(");");
7278 }
7279 </pre></code>
7280  *
7281  * @constructor
7282  * @param {Object} config A configuration object.
7283  */
7284 Roo.data.ScriptTagProxy = function(config){
7285     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7286     Roo.apply(this, config);
7287     this.head = document.getElementsByTagName("head")[0];
7288 };
7289
7290 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7291
7292 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7293     /**
7294      * @cfg {String} url The URL from which to request the data object.
7295      */
7296     /**
7297      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7298      */
7299     timeout : 30000,
7300     /**
7301      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7302      * the server the name of the callback function set up by the load call to process the returned data object.
7303      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7304      * javascript output which calls this named function passing the data object as its only parameter.
7305      */
7306     callbackParam : "callback",
7307     /**
7308      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7309      * name to the request.
7310      */
7311     nocache : true,
7312
7313     /**
7314      * Load data from the configured URL, read the data object into
7315      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7316      * process that block using the passed callback.
7317      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7318      * for the request to the remote server.
7319      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7320      * object into a block of Roo.data.Records.
7321      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7322      * The function must be passed <ul>
7323      * <li>The Record block object</li>
7324      * <li>The "arg" argument from the load function</li>
7325      * <li>A boolean success indicator</li>
7326      * </ul>
7327      * @param {Object} scope The scope in which to call the callback
7328      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7329      */
7330     load : function(params, reader, callback, scope, arg){
7331         if(this.fireEvent("beforeload", this, params) !== false){
7332
7333             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7334
7335             var url = this.url;
7336             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7337             if(this.nocache){
7338                 url += "&_dc=" + (new Date().getTime());
7339             }
7340             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7341             var trans = {
7342                 id : transId,
7343                 cb : "stcCallback"+transId,
7344                 scriptId : "stcScript"+transId,
7345                 params : params,
7346                 arg : arg,
7347                 url : url,
7348                 callback : callback,
7349                 scope : scope,
7350                 reader : reader
7351             };
7352             var conn = this;
7353
7354             window[trans.cb] = function(o){
7355                 conn.handleResponse(o, trans);
7356             };
7357
7358             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7359
7360             if(this.autoAbort !== false){
7361                 this.abort();
7362             }
7363
7364             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7365
7366             var script = document.createElement("script");
7367             script.setAttribute("src", url);
7368             script.setAttribute("type", "text/javascript");
7369             script.setAttribute("id", trans.scriptId);
7370             this.head.appendChild(script);
7371
7372             this.trans = trans;
7373         }else{
7374             callback.call(scope||this, null, arg, false);
7375         }
7376     },
7377
7378     // private
7379     isLoading : function(){
7380         return this.trans ? true : false;
7381     },
7382
7383     /**
7384      * Abort the current server request.
7385      */
7386     abort : function(){
7387         if(this.isLoading()){
7388             this.destroyTrans(this.trans);
7389         }
7390     },
7391
7392     // private
7393     destroyTrans : function(trans, isLoaded){
7394         this.head.removeChild(document.getElementById(trans.scriptId));
7395         clearTimeout(trans.timeoutId);
7396         if(isLoaded){
7397             window[trans.cb] = undefined;
7398             try{
7399                 delete window[trans.cb];
7400             }catch(e){}
7401         }else{
7402             // if hasn't been loaded, wait for load to remove it to prevent script error
7403             window[trans.cb] = function(){
7404                 window[trans.cb] = undefined;
7405                 try{
7406                     delete window[trans.cb];
7407                 }catch(e){}
7408             };
7409         }
7410     },
7411
7412     // private
7413     handleResponse : function(o, trans){
7414         this.trans = false;
7415         this.destroyTrans(trans, true);
7416         var result;
7417         try {
7418             result = trans.reader.readRecords(o);
7419         }catch(e){
7420             this.fireEvent("loadexception", this, o, trans.arg, e);
7421             trans.callback.call(trans.scope||window, null, trans.arg, false);
7422             return;
7423         }
7424         this.fireEvent("load", this, o, trans.arg);
7425         trans.callback.call(trans.scope||window, result, trans.arg, true);
7426     },
7427
7428     // private
7429     handleFailure : function(trans){
7430         this.trans = false;
7431         this.destroyTrans(trans, false);
7432         this.fireEvent("loadexception", this, null, trans.arg);
7433         trans.callback.call(trans.scope||window, null, trans.arg, false);
7434     }
7435 });/*
7436  * Based on:
7437  * Ext JS Library 1.1.1
7438  * Copyright(c) 2006-2007, Ext JS, LLC.
7439  *
7440  * Originally Released Under LGPL - original licence link has changed is not relivant.
7441  *
7442  * Fork - LGPL
7443  * <script type="text/javascript">
7444  */
7445
7446 /**
7447  * @class Roo.data.JsonReader
7448  * @extends Roo.data.DataReader
7449  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7450  * based on mappings in a provided Roo.data.Record constructor.
7451  * 
7452  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7453  * in the reply previously. 
7454  * 
7455  * <p>
7456  * Example code:
7457  * <pre><code>
7458 var RecordDef = Roo.data.Record.create([
7459     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7460     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7461 ]);
7462 var myReader = new Roo.data.JsonReader({
7463     totalProperty: "results",    // The property which contains the total dataset size (optional)
7464     root: "rows",                // The property which contains an Array of row objects
7465     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7466 }, RecordDef);
7467 </code></pre>
7468  * <p>
7469  * This would consume a JSON file like this:
7470  * <pre><code>
7471 { 'results': 2, 'rows': [
7472     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7473     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7474 }
7475 </code></pre>
7476  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7477  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7478  * paged from the remote server.
7479  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7480  * @cfg {String} root name of the property which contains the Array of row objects.
7481  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7482  * @constructor
7483  * Create a new JsonReader
7484  * @param {Object} meta Metadata configuration options
7485  * @param {Object} recordType Either an Array of field definition objects,
7486  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7487  */
7488 Roo.data.JsonReader = function(meta, recordType){
7489     
7490     meta = meta || {};
7491     // set some defaults:
7492     Roo.applyIf(meta, {
7493         totalProperty: 'total',
7494         successProperty : 'success',
7495         root : 'data',
7496         id : 'id'
7497     });
7498     
7499     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7500 };
7501 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7502     
7503     /**
7504      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7505      * Used by Store query builder to append _requestMeta to params.
7506      * 
7507      */
7508     metaFromRemote : false,
7509     /**
7510      * This method is only used by a DataProxy which has retrieved data from a remote server.
7511      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7512      * @return {Object} data A data block which is used by an Roo.data.Store object as
7513      * a cache of Roo.data.Records.
7514      */
7515     read : function(response){
7516         var json = response.responseText;
7517        
7518         var o = /* eval:var:o */ eval("("+json+")");
7519         if(!o) {
7520             throw {message: "JsonReader.read: Json object not found"};
7521         }
7522         
7523         if(o.metaData){
7524             
7525             delete this.ef;
7526             this.metaFromRemote = true;
7527             this.meta = o.metaData;
7528             this.recordType = Roo.data.Record.create(o.metaData.fields);
7529             this.onMetaChange(this.meta, this.recordType, o);
7530         }
7531         return this.readRecords(o);
7532     },
7533
7534     // private function a store will implement
7535     onMetaChange : function(meta, recordType, o){
7536
7537     },
7538
7539     /**
7540          * @ignore
7541          */
7542     simpleAccess: function(obj, subsc) {
7543         return obj[subsc];
7544     },
7545
7546         /**
7547          * @ignore
7548          */
7549     getJsonAccessor: function(){
7550         var re = /[\[\.]/;
7551         return function(expr) {
7552             try {
7553                 return(re.test(expr))
7554                     ? new Function("obj", "return obj." + expr)
7555                     : function(obj){
7556                         return obj[expr];
7557                     };
7558             } catch(e){}
7559             return Roo.emptyFn;
7560         };
7561     }(),
7562
7563     /**
7564      * Create a data block containing Roo.data.Records from an XML document.
7565      * @param {Object} o An object which contains an Array of row objects in the property specified
7566      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7567      * which contains the total size of the dataset.
7568      * @return {Object} data A data block which is used by an Roo.data.Store object as
7569      * a cache of Roo.data.Records.
7570      */
7571     readRecords : function(o){
7572         /**
7573          * After any data loads, the raw JSON data is available for further custom processing.
7574          * @type Object
7575          */
7576         this.o = o;
7577         var s = this.meta, Record = this.recordType,
7578             f = Record.prototype.fields, fi = f.items, fl = f.length;
7579
7580 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7581         if (!this.ef) {
7582             if(s.totalProperty) {
7583                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7584                 }
7585                 if(s.successProperty) {
7586                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7587                 }
7588                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7589                 if (s.id) {
7590                         var g = this.getJsonAccessor(s.id);
7591                         this.getId = function(rec) {
7592                                 var r = g(rec);
7593                                 return (r === undefined || r === "") ? null : r;
7594                         };
7595                 } else {
7596                         this.getId = function(){return null;};
7597                 }
7598             this.ef = [];
7599             for(var jj = 0; jj < fl; jj++){
7600                 f = fi[jj];
7601                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7602                 this.ef[jj] = this.getJsonAccessor(map);
7603             }
7604         }
7605
7606         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7607         if(s.totalProperty){
7608             var vt = parseInt(this.getTotal(o), 10);
7609             if(!isNaN(vt)){
7610                 totalRecords = vt;
7611             }
7612         }
7613         if(s.successProperty){
7614             var vs = this.getSuccess(o);
7615             if(vs === false || vs === 'false'){
7616                 success = false;
7617             }
7618         }
7619         var records = [];
7620             for(var i = 0; i < c; i++){
7621                     var n = root[i];
7622                 var values = {};
7623                 var id = this.getId(n);
7624                 for(var j = 0; j < fl; j++){
7625                     f = fi[j];
7626                 var v = this.ef[j](n);
7627                 if (!f.convert) {
7628                     Roo.log('missing convert for ' + f.name);
7629                     Roo.log(f);
7630                     continue;
7631                 }
7632                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7633                 }
7634                 var record = new Record(values, id);
7635                 record.json = n;
7636                 records[i] = record;
7637             }
7638             return {
7639             raw : o,
7640                 success : success,
7641                 records : records,
7642                 totalRecords : totalRecords
7643             };
7644     }
7645 });/*
7646  * Based on:
7647  * Ext JS Library 1.1.1
7648  * Copyright(c) 2006-2007, Ext JS, LLC.
7649  *
7650  * Originally Released Under LGPL - original licence link has changed is not relivant.
7651  *
7652  * Fork - LGPL
7653  * <script type="text/javascript">
7654  */
7655
7656 /**
7657  * @class Roo.data.ArrayReader
7658  * @extends Roo.data.DataReader
7659  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7660  * Each element of that Array represents a row of data fields. The
7661  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7662  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7663  * <p>
7664  * Example code:.
7665  * <pre><code>
7666 var RecordDef = Roo.data.Record.create([
7667     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7668     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7669 ]);
7670 var myReader = new Roo.data.ArrayReader({
7671     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7672 }, RecordDef);
7673 </code></pre>
7674  * <p>
7675  * This would consume an Array like this:
7676  * <pre><code>
7677 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7678   </code></pre>
7679  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7680  * @constructor
7681  * Create a new JsonReader
7682  * @param {Object} meta Metadata configuration options.
7683  * @param {Object} recordType Either an Array of field definition objects
7684  * as specified to {@link Roo.data.Record#create},
7685  * or an {@link Roo.data.Record} object
7686  * created using {@link Roo.data.Record#create}.
7687  */
7688 Roo.data.ArrayReader = function(meta, recordType){
7689     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7690 };
7691
7692 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7693     /**
7694      * Create a data block containing Roo.data.Records from an XML document.
7695      * @param {Object} o An Array of row objects which represents the dataset.
7696      * @return {Object} data A data block which is used by an Roo.data.Store object as
7697      * a cache of Roo.data.Records.
7698      */
7699     readRecords : function(o){
7700         var sid = this.meta ? this.meta.id : null;
7701         var recordType = this.recordType, fields = recordType.prototype.fields;
7702         var records = [];
7703         var root = o;
7704             for(var i = 0; i < root.length; i++){
7705                     var n = root[i];
7706                 var values = {};
7707                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7708                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7709                 var f = fields.items[j];
7710                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7711                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7712                 v = f.convert(v);
7713                 values[f.name] = v;
7714             }
7715                 var record = new recordType(values, id);
7716                 record.json = n;
7717                 records[records.length] = record;
7718             }
7719             return {
7720                 records : records,
7721                 totalRecords : records.length
7722             };
7723     }
7724 });/*
7725  * - LGPL
7726  * * 
7727  */
7728
7729 /**
7730  * @class Roo.bootstrap.ComboBox
7731  * @extends Roo.bootstrap.TriggerField
7732  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7733  * @cfg {Boolean} append (true|false) default false
7734  * @constructor
7735  * Create a new ComboBox.
7736  * @param {Object} config Configuration options
7737  */
7738 Roo.bootstrap.ComboBox = function(config){
7739     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7740     this.addEvents({
7741         /**
7742          * @event expand
7743          * Fires when the dropdown list is expanded
7744              * @param {Roo.bootstrap.ComboBox} combo This combo box
7745              */
7746         'expand' : true,
7747         /**
7748          * @event collapse
7749          * Fires when the dropdown list is collapsed
7750              * @param {Roo.bootstrap.ComboBox} combo This combo box
7751              */
7752         'collapse' : true,
7753         /**
7754          * @event beforeselect
7755          * Fires before a list item is selected. Return false to cancel the selection.
7756              * @param {Roo.bootstrap.ComboBox} combo This combo box
7757              * @param {Roo.data.Record} record The data record returned from the underlying store
7758              * @param {Number} index The index of the selected item in the dropdown list
7759              */
7760         'beforeselect' : true,
7761         /**
7762          * @event select
7763          * Fires when a list item is selected
7764              * @param {Roo.bootstrap.ComboBox} combo This combo box
7765              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7766              * @param {Number} index The index of the selected item in the dropdown list
7767              */
7768         'select' : true,
7769         /**
7770          * @event beforequery
7771          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7772          * The event object passed has these properties:
7773              * @param {Roo.bootstrap.ComboBox} combo This combo box
7774              * @param {String} query The query
7775              * @param {Boolean} forceAll true to force "all" query
7776              * @param {Boolean} cancel true to cancel the query
7777              * @param {Object} e The query event object
7778              */
7779         'beforequery': true,
7780          /**
7781          * @event add
7782          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7783              * @param {Roo.bootstrap.ComboBox} combo This combo box
7784              */
7785         'add' : true,
7786         /**
7787          * @event edit
7788          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7789              * @param {Roo.bootstrap.ComboBox} combo This combo box
7790              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7791              */
7792         'edit' : true,
7793         /**
7794          * @event remove
7795          * Fires when the remove value from the combobox array
7796              * @param {Roo.bootstrap.ComboBox} combo This combo box
7797              */
7798         'remove' : true
7799         
7800     });
7801     
7802     
7803     this.selectedIndex = -1;
7804     if(this.mode == 'local'){
7805         if(config.queryDelay === undefined){
7806             this.queryDelay = 10;
7807         }
7808         if(config.minChars === undefined){
7809             this.minChars = 0;
7810         }
7811     }
7812 };
7813
7814 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7815      
7816     /**
7817      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7818      * rendering into an Roo.Editor, defaults to false)
7819      */
7820     /**
7821      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7822      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7823      */
7824     /**
7825      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7826      */
7827     /**
7828      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7829      * the dropdown list (defaults to undefined, with no header element)
7830      */
7831
7832      /**
7833      * @cfg {String/Roo.Template} tpl The template to use to render the output
7834      */
7835      
7836      /**
7837      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7838      */
7839     listWidth: undefined,
7840     /**
7841      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7842      * mode = 'remote' or 'text' if mode = 'local')
7843      */
7844     displayField: undefined,
7845     /**
7846      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7847      * mode = 'remote' or 'value' if mode = 'local'). 
7848      * Note: use of a valueField requires the user make a selection
7849      * in order for a value to be mapped.
7850      */
7851     valueField: undefined,
7852     
7853     
7854     /**
7855      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7856      * field's data value (defaults to the underlying DOM element's name)
7857      */
7858     hiddenName: undefined,
7859     /**
7860      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7861      */
7862     listClass: '',
7863     /**
7864      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7865      */
7866     selectedClass: 'active',
7867     
7868     /**
7869      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7870      */
7871     shadow:'sides',
7872     /**
7873      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7874      * anchor positions (defaults to 'tl-bl')
7875      */
7876     listAlign: 'tl-bl?',
7877     /**
7878      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7879      */
7880     maxHeight: 300,
7881     /**
7882      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7883      * query specified by the allQuery config option (defaults to 'query')
7884      */
7885     triggerAction: 'query',
7886     /**
7887      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7888      * (defaults to 4, does not apply if editable = false)
7889      */
7890     minChars : 4,
7891     /**
7892      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7893      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7894      */
7895     typeAhead: false,
7896     /**
7897      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7898      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7899      */
7900     queryDelay: 500,
7901     /**
7902      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7903      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7904      */
7905     pageSize: 0,
7906     /**
7907      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7908      * when editable = true (defaults to false)
7909      */
7910     selectOnFocus:false,
7911     /**
7912      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7913      */
7914     queryParam: 'query',
7915     /**
7916      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7917      * when mode = 'remote' (defaults to 'Loading...')
7918      */
7919     loadingText: 'Loading...',
7920     /**
7921      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7922      */
7923     resizable: false,
7924     /**
7925      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7926      */
7927     handleHeight : 8,
7928     /**
7929      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7930      * traditional select (defaults to true)
7931      */
7932     editable: true,
7933     /**
7934      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7935      */
7936     allQuery: '',
7937     /**
7938      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7939      */
7940     mode: 'remote',
7941     /**
7942      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7943      * listWidth has a higher value)
7944      */
7945     minListWidth : 70,
7946     /**
7947      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7948      * allow the user to set arbitrary text into the field (defaults to false)
7949      */
7950     forceSelection:false,
7951     /**
7952      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7953      * if typeAhead = true (defaults to 250)
7954      */
7955     typeAheadDelay : 250,
7956     /**
7957      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7958      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7959      */
7960     valueNotFoundText : undefined,
7961     /**
7962      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7963      */
7964     blockFocus : false,
7965     
7966     /**
7967      * @cfg {Boolean} disableClear Disable showing of clear button.
7968      */
7969     disableClear : false,
7970     /**
7971      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7972      */
7973     alwaysQuery : false,
7974     
7975     /**
7976      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7977      */
7978     multiple : false,
7979     
7980     //private
7981     addicon : false,
7982     editicon: false,
7983     
7984     page: 0,
7985     hasQuery: false,
7986     append: false,
7987     loadNext: false,
7988     item: [],
7989     
7990     // element that contains real text value.. (when hidden is used..)
7991      
7992     // private
7993     initEvents: function(){
7994         
7995         if (!this.store) {
7996             throw "can not find store for combo";
7997         }
7998         this.store = Roo.factory(this.store, Roo.data);
7999         
8000         
8001         
8002         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8003         
8004         
8005         if(this.hiddenName){
8006             
8007             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8008             
8009             this.hiddenField.dom.value =
8010                 this.hiddenValue !== undefined ? this.hiddenValue :
8011                 this.value !== undefined ? this.value : '';
8012
8013             // prevent input submission
8014             this.el.dom.removeAttribute('name');
8015             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8016              
8017              
8018         }
8019         //if(Roo.isGecko){
8020         //    this.el.dom.setAttribute('autocomplete', 'off');
8021         //}
8022
8023         var cls = 'x-combo-list';
8024         this.list = this.el.select('ul.dropdown-menu',true).first();
8025
8026         //this.list = new Roo.Layer({
8027         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8028         //});
8029         
8030         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8031         this.list.setWidth(lw);
8032         
8033         this.list.on('mouseover', this.onViewOver, this);
8034         this.list.on('mousemove', this.onViewMove, this);
8035         
8036         this.list.on('scroll', this.onViewScroll, this);
8037         
8038         /*
8039         this.list.swallowEvent('mousewheel');
8040         this.assetHeight = 0;
8041
8042         if(this.title){
8043             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8044             this.assetHeight += this.header.getHeight();
8045         }
8046
8047         this.innerList = this.list.createChild({cls:cls+'-inner'});
8048         this.innerList.on('mouseover', this.onViewOver, this);
8049         this.innerList.on('mousemove', this.onViewMove, this);
8050         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8051         
8052         if(this.allowBlank && !this.pageSize && !this.disableClear){
8053             this.footer = this.list.createChild({cls:cls+'-ft'});
8054             this.pageTb = new Roo.Toolbar(this.footer);
8055            
8056         }
8057         if(this.pageSize){
8058             this.footer = this.list.createChild({cls:cls+'-ft'});
8059             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8060                     {pageSize: this.pageSize});
8061             
8062         }
8063         
8064         if (this.pageTb && this.allowBlank && !this.disableClear) {
8065             var _this = this;
8066             this.pageTb.add(new Roo.Toolbar.Fill(), {
8067                 cls: 'x-btn-icon x-btn-clear',
8068                 text: '&#160;',
8069                 handler: function()
8070                 {
8071                     _this.collapse();
8072                     _this.clearValue();
8073                     _this.onSelect(false, -1);
8074                 }
8075             });
8076         }
8077         if (this.footer) {
8078             this.assetHeight += this.footer.getHeight();
8079         }
8080         */
8081             
8082         if(!this.tpl){
8083             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8084         }
8085
8086         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8087             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8088         });
8089         //this.view.wrapEl.setDisplayed(false);
8090         this.view.on('click', this.onViewClick, this);
8091         
8092         
8093         
8094         this.store.on('beforeload', this.onBeforeLoad, this);
8095         this.store.on('load', this.onLoad, this);
8096         this.store.on('loadexception', this.onLoadException, this);
8097         /*
8098         if(this.resizable){
8099             this.resizer = new Roo.Resizable(this.list,  {
8100                pinned:true, handles:'se'
8101             });
8102             this.resizer.on('resize', function(r, w, h){
8103                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8104                 this.listWidth = w;
8105                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8106                 this.restrictHeight();
8107             }, this);
8108             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8109         }
8110         */
8111         if(!this.editable){
8112             this.editable = true;
8113             this.setEditable(false);
8114         }
8115         
8116         /*
8117         
8118         if (typeof(this.events.add.listeners) != 'undefined') {
8119             
8120             this.addicon = this.wrap.createChild(
8121                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8122        
8123             this.addicon.on('click', function(e) {
8124                 this.fireEvent('add', this);
8125             }, this);
8126         }
8127         if (typeof(this.events.edit.listeners) != 'undefined') {
8128             
8129             this.editicon = this.wrap.createChild(
8130                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8131             if (this.addicon) {
8132                 this.editicon.setStyle('margin-left', '40px');
8133             }
8134             this.editicon.on('click', function(e) {
8135                 
8136                 // we fire even  if inothing is selected..
8137                 this.fireEvent('edit', this, this.lastData );
8138                 
8139             }, this);
8140         }
8141         */
8142         
8143         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8144             "up" : function(e){
8145                 this.inKeyMode = true;
8146                 this.selectPrev();
8147             },
8148
8149             "down" : function(e){
8150                 if(!this.isExpanded()){
8151                     this.onTriggerClick();
8152                 }else{
8153                     this.inKeyMode = true;
8154                     this.selectNext();
8155                 }
8156             },
8157
8158             "enter" : function(e){
8159                 this.onViewClick();
8160                 //return true;
8161             },
8162
8163             "esc" : function(e){
8164                 this.collapse();
8165             },
8166
8167             "tab" : function(e){
8168                 this.collapse();
8169                 
8170                 if(this.fireEvent("specialkey", this, e)){
8171                     this.onViewClick(false);
8172                 }
8173                 
8174                 return true;
8175             },
8176
8177             scope : this,
8178
8179             doRelay : function(foo, bar, hname){
8180                 if(hname == 'down' || this.scope.isExpanded()){
8181                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8182                 }
8183                 return true;
8184             },
8185
8186             forceKeyDown: true
8187         });
8188         
8189         
8190         this.queryDelay = Math.max(this.queryDelay || 10,
8191                 this.mode == 'local' ? 10 : 250);
8192         
8193         
8194         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8195         
8196         if(this.typeAhead){
8197             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8198         }
8199         if(this.editable !== false){
8200             this.inputEl().on("keyup", this.onKeyUp, this);
8201         }
8202         if(this.forceSelection){
8203             this.on('blur', this.doForce, this);
8204         }
8205         
8206         if(this.multiple){
8207             this.choices = this.el.select('ul.select2-choices', true).first();
8208             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8209         }
8210     },
8211
8212     onDestroy : function(){
8213         if(this.view){
8214             this.view.setStore(null);
8215             this.view.el.removeAllListeners();
8216             this.view.el.remove();
8217             this.view.purgeListeners();
8218         }
8219         if(this.list){
8220             this.list.dom.innerHTML  = '';
8221         }
8222         if(this.store){
8223             this.store.un('beforeload', this.onBeforeLoad, this);
8224             this.store.un('load', this.onLoad, this);
8225             this.store.un('loadexception', this.onLoadException, this);
8226         }
8227         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8228     },
8229
8230     // private
8231     fireKey : function(e){
8232         if(e.isNavKeyPress() && !this.list.isVisible()){
8233             this.fireEvent("specialkey", this, e);
8234         }
8235     },
8236
8237     // private
8238     onResize: function(w, h){
8239 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8240 //        
8241 //        if(typeof w != 'number'){
8242 //            // we do not handle it!?!?
8243 //            return;
8244 //        }
8245 //        var tw = this.trigger.getWidth();
8246 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8247 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8248 //        var x = w - tw;
8249 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8250 //            
8251 //        //this.trigger.setStyle('left', x+'px');
8252 //        
8253 //        if(this.list && this.listWidth === undefined){
8254 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8255 //            this.list.setWidth(lw);
8256 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8257 //        }
8258         
8259     
8260         
8261     },
8262
8263     /**
8264      * Allow or prevent the user from directly editing the field text.  If false is passed,
8265      * the user will only be able to select from the items defined in the dropdown list.  This method
8266      * is the runtime equivalent of setting the 'editable' config option at config time.
8267      * @param {Boolean} value True to allow the user to directly edit the field text
8268      */
8269     setEditable : function(value){
8270         if(value == this.editable){
8271             return;
8272         }
8273         this.editable = value;
8274         if(!value){
8275             this.inputEl().dom.setAttribute('readOnly', true);
8276             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8277             this.inputEl().addClass('x-combo-noedit');
8278         }else{
8279             this.inputEl().dom.setAttribute('readOnly', false);
8280             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8281             this.inputEl().removeClass('x-combo-noedit');
8282         }
8283     },
8284
8285     // private
8286     
8287     onBeforeLoad : function(combo,opts){
8288         if(!this.hasFocus){
8289             return;
8290         }
8291          if (!opts.add) {
8292             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8293          }
8294         this.restrictHeight();
8295         this.selectedIndex = -1;
8296     },
8297
8298     // private
8299     onLoad : function(){
8300         
8301         this.hasQuery = false;
8302         
8303         if(!this.hasFocus){
8304             return;
8305         }
8306         
8307         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8308             this.loading.hide();
8309         }
8310         
8311         if(this.store.getCount() > 0){
8312             this.expand();
8313             this.restrictHeight();
8314             if(this.lastQuery == this.allQuery){
8315                 if(this.editable){
8316                     this.inputEl().dom.select();
8317                 }
8318                 if(!this.selectByValue(this.value, true)){
8319                     this.select(0, true);
8320                 }
8321             }else{
8322                 this.selectNext();
8323                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8324                     this.taTask.delay(this.typeAheadDelay);
8325                 }
8326             }
8327         }else{
8328             this.onEmptyResults();
8329         }
8330         
8331         //this.el.focus();
8332     },
8333     // private
8334     onLoadException : function()
8335     {
8336         this.hasQuery = false;
8337         
8338         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8339             this.loading.hide();
8340         }
8341         
8342         this.collapse();
8343         Roo.log(this.store.reader.jsonData);
8344         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8345             // fixme
8346             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8347         }
8348         
8349         
8350     },
8351     // private
8352     onTypeAhead : function(){
8353         if(this.store.getCount() > 0){
8354             var r = this.store.getAt(0);
8355             var newValue = r.data[this.displayField];
8356             var len = newValue.length;
8357             var selStart = this.getRawValue().length;
8358             
8359             if(selStart != len){
8360                 this.setRawValue(newValue);
8361                 this.selectText(selStart, newValue.length);
8362             }
8363         }
8364     },
8365
8366     // private
8367     onSelect : function(record, index){
8368         
8369         if(this.fireEvent('beforeselect', this, record, index) !== false){
8370         
8371             this.setFromData(index > -1 ? record.data : false);
8372             
8373             this.collapse();
8374             this.fireEvent('select', this, record, index);
8375         }
8376     },
8377
8378     /**
8379      * Returns the currently selected field value or empty string if no value is set.
8380      * @return {String} value The selected value
8381      */
8382     getValue : function(){
8383         
8384         if(this.multiple){
8385             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8386         }
8387         
8388         if(this.valueField){
8389             return typeof this.value != 'undefined' ? this.value : '';
8390         }else{
8391             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8392         }
8393     },
8394
8395     /**
8396      * Clears any text/value currently set in the field
8397      */
8398     clearValue : function(){
8399         if(this.hiddenField){
8400             this.hiddenField.dom.value = '';
8401         }
8402         this.value = '';
8403         this.setRawValue('');
8404         this.lastSelectionText = '';
8405         
8406     },
8407
8408     /**
8409      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8410      * will be displayed in the field.  If the value does not match the data value of an existing item,
8411      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8412      * Otherwise the field will be blank (although the value will still be set).
8413      * @param {String} value The value to match
8414      */
8415     setValue : function(v){
8416         if(this.multiple){
8417             this.syncValue();
8418             return;
8419         }
8420         
8421         var text = v;
8422         if(this.valueField){
8423             var r = this.findRecord(this.valueField, v);
8424             if(r){
8425                 text = r.data[this.displayField];
8426             }else if(this.valueNotFoundText !== undefined){
8427                 text = this.valueNotFoundText;
8428             }
8429         }
8430         this.lastSelectionText = text;
8431         if(this.hiddenField){
8432             this.hiddenField.dom.value = v;
8433         }
8434         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8435         this.value = v;
8436     },
8437     /**
8438      * @property {Object} the last set data for the element
8439      */
8440     
8441     lastData : false,
8442     /**
8443      * Sets the value of the field based on a object which is related to the record format for the store.
8444      * @param {Object} value the value to set as. or false on reset?
8445      */
8446     setFromData : function(o){
8447         
8448         if(this.multiple){
8449             this.addItem(o);
8450             return;
8451         }
8452             
8453         var dv = ''; // display value
8454         var vv = ''; // value value..
8455         this.lastData = o;
8456         if (this.displayField) {
8457             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8458         } else {
8459             // this is an error condition!!!
8460             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8461         }
8462         
8463         if(this.valueField){
8464             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8465         }
8466         
8467         if(this.hiddenField){
8468             this.hiddenField.dom.value = vv;
8469             
8470             this.lastSelectionText = dv;
8471             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8472             this.value = vv;
8473             return;
8474         }
8475         // no hidden field.. - we store the value in 'value', but still display
8476         // display field!!!!
8477         this.lastSelectionText = dv;
8478         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8479         this.value = vv;
8480         
8481         
8482     },
8483     // private
8484     reset : function(){
8485         // overridden so that last data is reset..
8486         this.setValue(this.originalValue);
8487         this.clearInvalid();
8488         this.lastData = false;
8489         if (this.view) {
8490             this.view.clearSelections();
8491         }
8492     },
8493     // private
8494     findRecord : function(prop, value){
8495         var record;
8496         if(this.store.getCount() > 0){
8497             this.store.each(function(r){
8498                 if(r.data[prop] == value){
8499                     record = r;
8500                     return false;
8501                 }
8502                 return true;
8503             });
8504         }
8505         return record;
8506     },
8507     
8508     getName: function()
8509     {
8510         // returns hidden if it's set..
8511         if (!this.rendered) {return ''};
8512         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8513         
8514     },
8515     // private
8516     onViewMove : function(e, t){
8517         this.inKeyMode = false;
8518     },
8519
8520     // private
8521     onViewOver : function(e, t){
8522         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8523             return;
8524         }
8525         var item = this.view.findItemFromChild(t);
8526         if(item){
8527             var index = this.view.indexOf(item);
8528             this.select(index, false);
8529         }
8530     },
8531
8532     // private
8533     onViewClick : function(doFocus)
8534     {
8535         var index = this.view.getSelectedIndexes()[0];
8536         var r = this.store.getAt(index);
8537         if(r){
8538             this.onSelect(r, index);
8539         }
8540         if(doFocus !== false && !this.blockFocus){
8541             this.inputEl().focus();
8542         }
8543     },
8544
8545     // private
8546     restrictHeight : function(){
8547         //this.innerList.dom.style.height = '';
8548         //var inner = this.innerList.dom;
8549         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8550         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8551         //this.list.beginUpdate();
8552         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8553         this.list.alignTo(this.inputEl(), this.listAlign);
8554         //this.list.endUpdate();
8555     },
8556
8557     // private
8558     onEmptyResults : function(){
8559         this.collapse();
8560     },
8561
8562     /**
8563      * Returns true if the dropdown list is expanded, else false.
8564      */
8565     isExpanded : function(){
8566         return this.list.isVisible();
8567     },
8568
8569     /**
8570      * Select an item in the dropdown list by its data value. 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 {String} value The data value of the 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      * @return {Boolean} True if the value matched an item in the list, else false
8576      */
8577     selectByValue : function(v, scrollIntoView){
8578         if(v !== undefined && v !== null){
8579             var r = this.findRecord(this.valueField || this.displayField, v);
8580             if(r){
8581                 this.select(this.store.indexOf(r), scrollIntoView);
8582                 return true;
8583             }
8584         }
8585         return false;
8586     },
8587
8588     /**
8589      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8590      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8591      * @param {Number} index The zero-based index of the list item to select
8592      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8593      * selected item if it is not currently in view (defaults to true)
8594      */
8595     select : function(index, scrollIntoView){
8596         this.selectedIndex = index;
8597         this.view.select(index);
8598         if(scrollIntoView !== false){
8599             var el = this.view.getNode(index);
8600             if(el){
8601                 //this.innerList.scrollChildIntoView(el, false);
8602                 
8603             }
8604         }
8605     },
8606
8607     // private
8608     selectNext : function(){
8609         var ct = this.store.getCount();
8610         if(ct > 0){
8611             if(this.selectedIndex == -1){
8612                 this.select(0);
8613             }else if(this.selectedIndex < ct-1){
8614                 this.select(this.selectedIndex+1);
8615             }
8616         }
8617     },
8618
8619     // private
8620     selectPrev : function(){
8621         var ct = this.store.getCount();
8622         if(ct > 0){
8623             if(this.selectedIndex == -1){
8624                 this.select(0);
8625             }else if(this.selectedIndex != 0){
8626                 this.select(this.selectedIndex-1);
8627             }
8628         }
8629     },
8630
8631     // private
8632     onKeyUp : function(e){
8633         if(this.editable !== false && !e.isSpecialKey()){
8634             this.lastKey = e.getKey();
8635             this.dqTask.delay(this.queryDelay);
8636         }
8637     },
8638
8639     // private
8640     validateBlur : function(){
8641         return !this.list || !this.list.isVisible();   
8642     },
8643
8644     // private
8645     initQuery : function(){
8646         this.doQuery(this.getRawValue());
8647     },
8648
8649     // private
8650     doForce : function(){
8651         if(this.el.dom.value.length > 0){
8652             this.el.dom.value =
8653                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8654              
8655         }
8656     },
8657
8658     /**
8659      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8660      * query allowing the query action to be canceled if needed.
8661      * @param {String} query The SQL query to execute
8662      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8663      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8664      * saved in the current store (defaults to false)
8665      */
8666     doQuery : function(q, forceAll){
8667         
8668         if(q === undefined || q === null){
8669             q = '';
8670         }
8671         var qe = {
8672             query: q,
8673             forceAll: forceAll,
8674             combo: this,
8675             cancel:false
8676         };
8677         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8678             return false;
8679         }
8680         q = qe.query;
8681         
8682         forceAll = qe.forceAll;
8683         if(forceAll === true || (q.length >= this.minChars)){
8684             
8685             this.hasQuery = true;
8686             
8687             if(this.lastQuery != q || this.alwaysQuery){
8688                 this.lastQuery = q;
8689                 if(this.mode == 'local'){
8690                     this.selectedIndex = -1;
8691                     if(forceAll){
8692                         this.store.clearFilter();
8693                     }else{
8694                         this.store.filter(this.displayField, q);
8695                     }
8696                     this.onLoad();
8697                 }else{
8698                     this.store.baseParams[this.queryParam] = q;
8699                     
8700                     var options = {params : this.getParams(q)};
8701                     
8702                     if(this.loadNext){
8703                         options.add = true;
8704                         options.params.start = this.page * this.pageSize;
8705                     }
8706                     
8707                     this.store.load(options);
8708                     this.expand();
8709                 }
8710             }else{
8711                 this.selectedIndex = -1;
8712                 this.onLoad();   
8713             }
8714         }
8715         
8716         this.loadNext = false;
8717     },
8718
8719     // private
8720     getParams : function(q){
8721         var p = {};
8722         //p[this.queryParam] = q;
8723         
8724         if(this.pageSize){
8725             p.start = 0;
8726             p.limit = this.pageSize;
8727         }
8728         return p;
8729     },
8730
8731     /**
8732      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8733      */
8734     collapse : function(){
8735         if(!this.isExpanded()){
8736             return;
8737         }
8738         
8739         this.list.hide();
8740         Roo.get(document).un('mousedown', this.collapseIf, this);
8741         Roo.get(document).un('mousewheel', this.collapseIf, this);
8742         if (!this.editable) {
8743             Roo.get(document).un('keydown', this.listKeyPress, this);
8744         }
8745         this.fireEvent('collapse', this);
8746     },
8747
8748     // private
8749     collapseIf : function(e){
8750         var in_combo  = e.within(this.el);
8751         var in_list =  e.within(this.list);
8752         
8753         if (in_combo || in_list) {
8754             //e.stopPropagation();
8755             return;
8756         }
8757
8758         this.collapse();
8759         
8760     },
8761
8762     /**
8763      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8764      */
8765     expand : function(){
8766        
8767         if(this.isExpanded() || !this.hasFocus){
8768             return;
8769         }
8770          Roo.log('expand');
8771         this.list.alignTo(this.inputEl(), this.listAlign);
8772         this.list.show();
8773         Roo.get(document).on('mousedown', this.collapseIf, this);
8774         Roo.get(document).on('mousewheel', this.collapseIf, this);
8775         if (!this.editable) {
8776             Roo.get(document).on('keydown', this.listKeyPress, this);
8777         }
8778         
8779         this.fireEvent('expand', this);
8780     },
8781
8782     // private
8783     // Implements the default empty TriggerField.onTriggerClick function
8784     onTriggerClick : function()
8785     {
8786         Roo.log('trigger click');
8787         
8788         if(this.disabled){
8789             return;
8790         }
8791         
8792         this.page = 0;
8793         this.loadNext = false;
8794         
8795         if(this.isExpanded()){
8796             this.collapse();
8797             if (!this.blockFocus) {
8798                 this.inputEl().focus();
8799             }
8800             
8801         }else {
8802             this.hasFocus = true;
8803             if(this.triggerAction == 'all') {
8804                 this.doQuery(this.allQuery, true);
8805             } else {
8806                 this.doQuery(this.getRawValue());
8807             }
8808             if (!this.blockFocus) {
8809                 this.inputEl().focus();
8810             }
8811         }
8812     },
8813     listKeyPress : function(e)
8814     {
8815         //Roo.log('listkeypress');
8816         // scroll to first matching element based on key pres..
8817         if (e.isSpecialKey()) {
8818             return false;
8819         }
8820         var k = String.fromCharCode(e.getKey()).toUpperCase();
8821         //Roo.log(k);
8822         var match  = false;
8823         var csel = this.view.getSelectedNodes();
8824         var cselitem = false;
8825         if (csel.length) {
8826             var ix = this.view.indexOf(csel[0]);
8827             cselitem  = this.store.getAt(ix);
8828             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8829                 cselitem = false;
8830             }
8831             
8832         }
8833         
8834         this.store.each(function(v) { 
8835             if (cselitem) {
8836                 // start at existing selection.
8837                 if (cselitem.id == v.id) {
8838                     cselitem = false;
8839                 }
8840                 return true;
8841             }
8842                 
8843             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8844                 match = this.store.indexOf(v);
8845                 return false;
8846             }
8847             return true;
8848         }, this);
8849         
8850         if (match === false) {
8851             return true; // no more action?
8852         }
8853         // scroll to?
8854         this.view.select(match);
8855         var sn = Roo.get(this.view.getSelectedNodes()[0])
8856         //sn.scrollIntoView(sn.dom.parentNode, false);
8857     },
8858     
8859     onViewScroll : function(e, t){
8860         
8861         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8862             return;
8863         }
8864         
8865         this.hasQuery = true;
8866         
8867         this.loading = this.list.select('.loading', true).first();
8868         
8869         if(this.loading === null){
8870             this.list.createChild({
8871                 tag: 'div',
8872                 cls: 'loading select2-more-results select2-active',
8873                 html: 'Loading more results...'
8874             })
8875             
8876             this.loading = this.list.select('.loading', true).first();
8877             
8878             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8879             
8880             this.loading.hide();
8881         }
8882         
8883         this.loading.show();
8884         
8885         var _combo = this;
8886         
8887         this.page++;
8888         this.loadNext = true;
8889         
8890         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8891         
8892         return;
8893     },
8894     
8895     addItem : function(o)
8896     {   
8897         var dv = ''; // display value
8898         
8899         if (this.displayField) {
8900             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8901         } else {
8902             // this is an error condition!!!
8903             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8904         }
8905         
8906         if(!dv.length){
8907             return;
8908         }
8909         
8910         var choice = this.choices.createChild({
8911             tag: 'li',
8912             cls: 'select2-search-choice',
8913             cn: [
8914                 {
8915                     tag: 'div',
8916                     html: dv
8917                 },
8918                 {
8919                     tag: 'a',
8920                     href: '#',
8921                     cls: 'select2-search-choice-close',
8922                     tabindex: '-1'
8923                 }
8924             ]
8925             
8926         }, this.searchField);
8927         
8928         var close = choice.select('a.select2-search-choice-close', true).first()
8929         
8930         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8931         
8932         this.item.push(o);
8933         this.lastData = o;
8934         
8935         this.syncValue();
8936         
8937         this.inputEl().dom.value = '';
8938         
8939     },
8940     
8941     onRemoveItem : function(e, _self, o)
8942     {
8943         Roo.log('remove item');
8944         var index = this.item.indexOf(o.data) * 1;
8945         
8946         if( index < 0){
8947             Roo.log('not this item?!');
8948             return;
8949         }
8950         
8951         this.item.splice(index, 1);
8952         o.item.remove();
8953         
8954         this.syncValue();
8955         
8956         this.fireEvent('remove', this);
8957         
8958     },
8959     
8960     syncValue : function()
8961     {
8962         if(!this.item.length){
8963             this.clearValue();
8964             return;
8965         }
8966             
8967         var value = [];
8968         var _this = this;
8969         Roo.each(this.item, function(i){
8970             if(_this.valueField){
8971                 value.push(i[_this.valueField]);
8972                 return;
8973             }
8974
8975             value.push(i);
8976         });
8977
8978         this.value = value.join(',');
8979
8980         if(this.hiddenField){
8981             this.hiddenField.dom.value = this.value;
8982         }
8983     },
8984     
8985     clearItem : function()
8986     {
8987         if(!this.multiple){
8988             return;
8989         }
8990         
8991         this.item = [];
8992         
8993         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
8994            c.remove();
8995         });
8996         
8997         this.syncValue();
8998     }
8999     
9000     
9001
9002     /** 
9003     * @cfg {Boolean} grow 
9004     * @hide 
9005     */
9006     /** 
9007     * @cfg {Number} growMin 
9008     * @hide 
9009     */
9010     /** 
9011     * @cfg {Number} growMax 
9012     * @hide 
9013     */
9014     /**
9015      * @hide
9016      * @method autoSize
9017      */
9018 });
9019 /*
9020  * Based on:
9021  * Ext JS Library 1.1.1
9022  * Copyright(c) 2006-2007, Ext JS, LLC.
9023  *
9024  * Originally Released Under LGPL - original licence link has changed is not relivant.
9025  *
9026  * Fork - LGPL
9027  * <script type="text/javascript">
9028  */
9029
9030 /**
9031  * @class Roo.View
9032  * @extends Roo.util.Observable
9033  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9034  * This class also supports single and multi selection modes. <br>
9035  * Create a data model bound view:
9036  <pre><code>
9037  var store = new Roo.data.Store(...);
9038
9039  var view = new Roo.View({
9040     el : "my-element",
9041     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9042  
9043     singleSelect: true,
9044     selectedClass: "ydataview-selected",
9045     store: store
9046  });
9047
9048  // listen for node click?
9049  view.on("click", function(vw, index, node, e){
9050  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9051  });
9052
9053  // load XML data
9054  dataModel.load("foobar.xml");
9055  </code></pre>
9056  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9057  * <br><br>
9058  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9059  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9060  * 
9061  * Note: old style constructor is still suported (container, template, config)
9062  * 
9063  * @constructor
9064  * Create a new View
9065  * @param {Object} config The config object
9066  * 
9067  */
9068 Roo.View = function(config, depreciated_tpl, depreciated_config){
9069     
9070     if (typeof(depreciated_tpl) == 'undefined') {
9071         // new way.. - universal constructor.
9072         Roo.apply(this, config);
9073         this.el  = Roo.get(this.el);
9074     } else {
9075         // old format..
9076         this.el  = Roo.get(config);
9077         this.tpl = depreciated_tpl;
9078         Roo.apply(this, depreciated_config);
9079     }
9080     this.wrapEl  = this.el.wrap().wrap();
9081     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9082     
9083     
9084     if(typeof(this.tpl) == "string"){
9085         this.tpl = new Roo.Template(this.tpl);
9086     } else {
9087         // support xtype ctors..
9088         this.tpl = new Roo.factory(this.tpl, Roo);
9089     }
9090     
9091     
9092     this.tpl.compile();
9093    
9094   
9095     
9096      
9097     /** @private */
9098     this.addEvents({
9099         /**
9100          * @event beforeclick
9101          * Fires before a click is processed. Returns false to cancel the default action.
9102          * @param {Roo.View} this
9103          * @param {Number} index The index of the target node
9104          * @param {HTMLElement} node The target node
9105          * @param {Roo.EventObject} e The raw event object
9106          */
9107             "beforeclick" : true,
9108         /**
9109          * @event click
9110          * Fires when a template node is clicked.
9111          * @param {Roo.View} this
9112          * @param {Number} index The index of the target node
9113          * @param {HTMLElement} node The target node
9114          * @param {Roo.EventObject} e The raw event object
9115          */
9116             "click" : true,
9117         /**
9118          * @event dblclick
9119          * Fires when a template node is double clicked.
9120          * @param {Roo.View} this
9121          * @param {Number} index The index of the target node
9122          * @param {HTMLElement} node The target node
9123          * @param {Roo.EventObject} e The raw event object
9124          */
9125             "dblclick" : true,
9126         /**
9127          * @event contextmenu
9128          * Fires when a template node is right clicked.
9129          * @param {Roo.View} this
9130          * @param {Number} index The index of the target node
9131          * @param {HTMLElement} node The target node
9132          * @param {Roo.EventObject} e The raw event object
9133          */
9134             "contextmenu" : true,
9135         /**
9136          * @event selectionchange
9137          * Fires when the selected nodes change.
9138          * @param {Roo.View} this
9139          * @param {Array} selections Array of the selected nodes
9140          */
9141             "selectionchange" : true,
9142     
9143         /**
9144          * @event beforeselect
9145          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9146          * @param {Roo.View} this
9147          * @param {HTMLElement} node The node to be selected
9148          * @param {Array} selections Array of currently selected nodes
9149          */
9150             "beforeselect" : true,
9151         /**
9152          * @event preparedata
9153          * Fires on every row to render, to allow you to change the data.
9154          * @param {Roo.View} this
9155          * @param {Object} data to be rendered (change this)
9156          */
9157           "preparedata" : true
9158           
9159           
9160         });
9161
9162
9163
9164     this.el.on({
9165         "click": this.onClick,
9166         "dblclick": this.onDblClick,
9167         "contextmenu": this.onContextMenu,
9168         scope:this
9169     });
9170
9171     this.selections = [];
9172     this.nodes = [];
9173     this.cmp = new Roo.CompositeElementLite([]);
9174     if(this.store){
9175         this.store = Roo.factory(this.store, Roo.data);
9176         this.setStore(this.store, true);
9177     }
9178     
9179     if ( this.footer && this.footer.xtype) {
9180            
9181          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9182         
9183         this.footer.dataSource = this.store
9184         this.footer.container = fctr;
9185         this.footer = Roo.factory(this.footer, Roo);
9186         fctr.insertFirst(this.el);
9187         
9188         // this is a bit insane - as the paging toolbar seems to detach the el..
9189 //        dom.parentNode.parentNode.parentNode
9190          // they get detached?
9191     }
9192     
9193     
9194     Roo.View.superclass.constructor.call(this);
9195     
9196     
9197 };
9198
9199 Roo.extend(Roo.View, Roo.util.Observable, {
9200     
9201      /**
9202      * @cfg {Roo.data.Store} store Data store to load data from.
9203      */
9204     store : false,
9205     
9206     /**
9207      * @cfg {String|Roo.Element} el The container element.
9208      */
9209     el : '',
9210     
9211     /**
9212      * @cfg {String|Roo.Template} tpl The template used by this View 
9213      */
9214     tpl : false,
9215     /**
9216      * @cfg {String} dataName the named area of the template to use as the data area
9217      *                          Works with domtemplates roo-name="name"
9218      */
9219     dataName: false,
9220     /**
9221      * @cfg {String} selectedClass The css class to add to selected nodes
9222      */
9223     selectedClass : "x-view-selected",
9224      /**
9225      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9226      */
9227     emptyText : "",
9228     
9229     /**
9230      * @cfg {String} text to display on mask (default Loading)
9231      */
9232     mask : false,
9233     /**
9234      * @cfg {Boolean} multiSelect Allow multiple selection
9235      */
9236     multiSelect : false,
9237     /**
9238      * @cfg {Boolean} singleSelect Allow single selection
9239      */
9240     singleSelect:  false,
9241     
9242     /**
9243      * @cfg {Boolean} toggleSelect - selecting 
9244      */
9245     toggleSelect : false,
9246     
9247     /**
9248      * Returns the element this view is bound to.
9249      * @return {Roo.Element}
9250      */
9251     getEl : function(){
9252         return this.wrapEl;
9253     },
9254     
9255     
9256
9257     /**
9258      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9259      */
9260     refresh : function(){
9261         Roo.log('refresh');
9262         var t = this.tpl;
9263         
9264         // if we are using something like 'domtemplate', then
9265         // the what gets used is:
9266         // t.applySubtemplate(NAME, data, wrapping data..)
9267         // the outer template then get' applied with
9268         //     the store 'extra data'
9269         // and the body get's added to the
9270         //      roo-name="data" node?
9271         //      <span class='roo-tpl-{name}'></span> ?????
9272         
9273         
9274         
9275         this.clearSelections();
9276         this.el.update("");
9277         var html = [];
9278         var records = this.store.getRange();
9279         if(records.length < 1) {
9280             
9281             // is this valid??  = should it render a template??
9282             
9283             this.el.update(this.emptyText);
9284             return;
9285         }
9286         var el = this.el;
9287         if (this.dataName) {
9288             this.el.update(t.apply(this.store.meta)); //????
9289             el = this.el.child('.roo-tpl-' + this.dataName);
9290         }
9291         
9292         for(var i = 0, len = records.length; i < len; i++){
9293             var data = this.prepareData(records[i].data, i, records[i]);
9294             this.fireEvent("preparedata", this, data, i, records[i]);
9295             html[html.length] = Roo.util.Format.trim(
9296                 this.dataName ?
9297                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9298                     t.apply(data)
9299             );
9300         }
9301         
9302         
9303         
9304         el.update(html.join(""));
9305         this.nodes = el.dom.childNodes;
9306         this.updateIndexes(0);
9307     },
9308     
9309
9310     /**
9311      * Function to override to reformat the data that is sent to
9312      * the template for each node.
9313      * DEPRICATED - use the preparedata event handler.
9314      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9315      * a JSON object for an UpdateManager bound view).
9316      */
9317     prepareData : function(data, index, record)
9318     {
9319         this.fireEvent("preparedata", this, data, index, record);
9320         return data;
9321     },
9322
9323     onUpdate : function(ds, record){
9324          Roo.log('on update');   
9325         this.clearSelections();
9326         var index = this.store.indexOf(record);
9327         var n = this.nodes[index];
9328         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9329         n.parentNode.removeChild(n);
9330         this.updateIndexes(index, index);
9331     },
9332
9333     
9334     
9335 // --------- FIXME     
9336     onAdd : function(ds, records, index)
9337     {
9338         Roo.log(['on Add', ds, records, index] );        
9339         this.clearSelections();
9340         if(this.nodes.length == 0){
9341             this.refresh();
9342             return;
9343         }
9344         var n = this.nodes[index];
9345         for(var i = 0, len = records.length; i < len; i++){
9346             var d = this.prepareData(records[i].data, i, records[i]);
9347             if(n){
9348                 this.tpl.insertBefore(n, d);
9349             }else{
9350                 
9351                 this.tpl.append(this.el, d);
9352             }
9353         }
9354         this.updateIndexes(index);
9355     },
9356
9357     onRemove : function(ds, record, index){
9358         Roo.log('onRemove');
9359         this.clearSelections();
9360         var el = this.dataName  ?
9361             this.el.child('.roo-tpl-' + this.dataName) :
9362             this.el; 
9363         
9364         el.dom.removeChild(this.nodes[index]);
9365         this.updateIndexes(index);
9366     },
9367
9368     /**
9369      * Refresh an individual node.
9370      * @param {Number} index
9371      */
9372     refreshNode : function(index){
9373         this.onUpdate(this.store, this.store.getAt(index));
9374     },
9375
9376     updateIndexes : function(startIndex, endIndex){
9377         var ns = this.nodes;
9378         startIndex = startIndex || 0;
9379         endIndex = endIndex || ns.length - 1;
9380         for(var i = startIndex; i <= endIndex; i++){
9381             ns[i].nodeIndex = i;
9382         }
9383     },
9384
9385     /**
9386      * Changes the data store this view uses and refresh the view.
9387      * @param {Store} store
9388      */
9389     setStore : function(store, initial){
9390         if(!initial && this.store){
9391             this.store.un("datachanged", this.refresh);
9392             this.store.un("add", this.onAdd);
9393             this.store.un("remove", this.onRemove);
9394             this.store.un("update", this.onUpdate);
9395             this.store.un("clear", this.refresh);
9396             this.store.un("beforeload", this.onBeforeLoad);
9397             this.store.un("load", this.onLoad);
9398             this.store.un("loadexception", this.onLoad);
9399         }
9400         if(store){
9401           
9402             store.on("datachanged", this.refresh, this);
9403             store.on("add", this.onAdd, this);
9404             store.on("remove", this.onRemove, this);
9405             store.on("update", this.onUpdate, this);
9406             store.on("clear", this.refresh, this);
9407             store.on("beforeload", this.onBeforeLoad, this);
9408             store.on("load", this.onLoad, this);
9409             store.on("loadexception", this.onLoad, this);
9410         }
9411         
9412         if(store){
9413             this.refresh();
9414         }
9415     },
9416     /**
9417      * onbeforeLoad - masks the loading area.
9418      *
9419      */
9420     onBeforeLoad : function(store,opts)
9421     {
9422          Roo.log('onBeforeLoad');   
9423         if (!opts.add) {
9424             this.el.update("");
9425         }
9426         this.el.mask(this.mask ? this.mask : "Loading" ); 
9427     },
9428     onLoad : function ()
9429     {
9430         this.el.unmask();
9431     },
9432     
9433
9434     /**
9435      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9436      * @param {HTMLElement} node
9437      * @return {HTMLElement} The template node
9438      */
9439     findItemFromChild : function(node){
9440         var el = this.dataName  ?
9441             this.el.child('.roo-tpl-' + this.dataName,true) :
9442             this.el.dom; 
9443         
9444         if(!node || node.parentNode == el){
9445                     return node;
9446             }
9447             var p = node.parentNode;
9448             while(p && p != el){
9449             if(p.parentNode == el){
9450                 return p;
9451             }
9452             p = p.parentNode;
9453         }
9454             return null;
9455     },
9456
9457     /** @ignore */
9458     onClick : function(e){
9459         var item = this.findItemFromChild(e.getTarget());
9460         if(item){
9461             var index = this.indexOf(item);
9462             if(this.onItemClick(item, index, e) !== false){
9463                 this.fireEvent("click", this, index, item, e);
9464             }
9465         }else{
9466             this.clearSelections();
9467         }
9468     },
9469
9470     /** @ignore */
9471     onContextMenu : function(e){
9472         var item = this.findItemFromChild(e.getTarget());
9473         if(item){
9474             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9475         }
9476     },
9477
9478     /** @ignore */
9479     onDblClick : function(e){
9480         var item = this.findItemFromChild(e.getTarget());
9481         if(item){
9482             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9483         }
9484     },
9485
9486     onItemClick : function(item, index, e)
9487     {
9488         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9489             return false;
9490         }
9491         if (this.toggleSelect) {
9492             var m = this.isSelected(item) ? 'unselect' : 'select';
9493             Roo.log(m);
9494             var _t = this;
9495             _t[m](item, true, false);
9496             return true;
9497         }
9498         if(this.multiSelect || this.singleSelect){
9499             if(this.multiSelect && e.shiftKey && this.lastSelection){
9500                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9501             }else{
9502                 this.select(item, this.multiSelect && e.ctrlKey);
9503                 this.lastSelection = item;
9504             }
9505             e.preventDefault();
9506         }
9507         return true;
9508     },
9509
9510     /**
9511      * Get the number of selected nodes.
9512      * @return {Number}
9513      */
9514     getSelectionCount : function(){
9515         return this.selections.length;
9516     },
9517
9518     /**
9519      * Get the currently selected nodes.
9520      * @return {Array} An array of HTMLElements
9521      */
9522     getSelectedNodes : function(){
9523         return this.selections;
9524     },
9525
9526     /**
9527      * Get the indexes of the selected nodes.
9528      * @return {Array}
9529      */
9530     getSelectedIndexes : function(){
9531         var indexes = [], s = this.selections;
9532         for(var i = 0, len = s.length; i < len; i++){
9533             indexes.push(s[i].nodeIndex);
9534         }
9535         return indexes;
9536     },
9537
9538     /**
9539      * Clear all selections
9540      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9541      */
9542     clearSelections : function(suppressEvent){
9543         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9544             this.cmp.elements = this.selections;
9545             this.cmp.removeClass(this.selectedClass);
9546             this.selections = [];
9547             if(!suppressEvent){
9548                 this.fireEvent("selectionchange", this, this.selections);
9549             }
9550         }
9551     },
9552
9553     /**
9554      * Returns true if the passed node is selected
9555      * @param {HTMLElement/Number} node The node or node index
9556      * @return {Boolean}
9557      */
9558     isSelected : function(node){
9559         var s = this.selections;
9560         if(s.length < 1){
9561             return false;
9562         }
9563         node = this.getNode(node);
9564         return s.indexOf(node) !== -1;
9565     },
9566
9567     /**
9568      * Selects nodes.
9569      * @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
9570      * @param {Boolean} keepExisting (optional) true to keep existing selections
9571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9572      */
9573     select : function(nodeInfo, keepExisting, suppressEvent){
9574         if(nodeInfo instanceof Array){
9575             if(!keepExisting){
9576                 this.clearSelections(true);
9577             }
9578             for(var i = 0, len = nodeInfo.length; i < len; i++){
9579                 this.select(nodeInfo[i], true, true);
9580             }
9581             return;
9582         } 
9583         var node = this.getNode(nodeInfo);
9584         if(!node || this.isSelected(node)){
9585             return; // already selected.
9586         }
9587         if(!keepExisting){
9588             this.clearSelections(true);
9589         }
9590         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9591             Roo.fly(node).addClass(this.selectedClass);
9592             this.selections.push(node);
9593             if(!suppressEvent){
9594                 this.fireEvent("selectionchange", this, this.selections);
9595             }
9596         }
9597         
9598         
9599     },
9600       /**
9601      * Unselects nodes.
9602      * @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
9603      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9604      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9605      */
9606     unselect : function(nodeInfo, keepExisting, suppressEvent)
9607     {
9608         if(nodeInfo instanceof Array){
9609             Roo.each(this.selections, function(s) {
9610                 this.unselect(s, nodeInfo);
9611             }, this);
9612             return;
9613         }
9614         var node = this.getNode(nodeInfo);
9615         if(!node || !this.isSelected(node)){
9616             Roo.log("not selected");
9617             return; // not selected.
9618         }
9619         // fireevent???
9620         var ns = [];
9621         Roo.each(this.selections, function(s) {
9622             if (s == node ) {
9623                 Roo.fly(node).removeClass(this.selectedClass);
9624
9625                 return;
9626             }
9627             ns.push(s);
9628         },this);
9629         
9630         this.selections= ns;
9631         this.fireEvent("selectionchange", this, this.selections);
9632     },
9633
9634     /**
9635      * Gets a template node.
9636      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9637      * @return {HTMLElement} The node or null if it wasn't found
9638      */
9639     getNode : function(nodeInfo){
9640         if(typeof nodeInfo == "string"){
9641             return document.getElementById(nodeInfo);
9642         }else if(typeof nodeInfo == "number"){
9643             return this.nodes[nodeInfo];
9644         }
9645         return nodeInfo;
9646     },
9647
9648     /**
9649      * Gets a range template nodes.
9650      * @param {Number} startIndex
9651      * @param {Number} endIndex
9652      * @return {Array} An array of nodes
9653      */
9654     getNodes : function(start, end){
9655         var ns = this.nodes;
9656         start = start || 0;
9657         end = typeof end == "undefined" ? ns.length - 1 : end;
9658         var nodes = [];
9659         if(start <= end){
9660             for(var i = start; i <= end; i++){
9661                 nodes.push(ns[i]);
9662             }
9663         } else{
9664             for(var i = start; i >= end; i--){
9665                 nodes.push(ns[i]);
9666             }
9667         }
9668         return nodes;
9669     },
9670
9671     /**
9672      * Finds the index of the passed node
9673      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9674      * @return {Number} The index of the node or -1
9675      */
9676     indexOf : function(node){
9677         node = this.getNode(node);
9678         if(typeof node.nodeIndex == "number"){
9679             return node.nodeIndex;
9680         }
9681         var ns = this.nodes;
9682         for(var i = 0, len = ns.length; i < len; i++){
9683             if(ns[i] == node){
9684                 return i;
9685             }
9686         }
9687         return -1;
9688     }
9689 });
9690 /*
9691  * - LGPL
9692  *
9693  * based on jquery fullcalendar
9694  * 
9695  */
9696
9697 Roo.bootstrap = Roo.bootstrap || {};
9698 /**
9699  * @class Roo.bootstrap.Calendar
9700  * @extends Roo.bootstrap.Component
9701  * Bootstrap Calendar class
9702  * @cfg {Boolean} loadMask (true|false) default false
9703     
9704  * @constructor
9705  * Create a new Container
9706  * @param {Object} config The config object
9707  */
9708
9709
9710
9711 Roo.bootstrap.Calendar = function(config){
9712     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9713      this.addEvents({
9714         /**
9715              * @event select
9716              * Fires when a date is selected
9717              * @param {DatePicker} this
9718              * @param {Date} date The selected date
9719              */
9720         'select': true,
9721         /**
9722              * @event monthchange
9723              * Fires when the displayed month changes 
9724              * @param {DatePicker} this
9725              * @param {Date} date The selected month
9726              */
9727         'monthchange': true,
9728         /**
9729              * @event evententer
9730              * Fires when mouse over an event
9731              * @param {Calendar} this
9732              * @param {event} Event
9733              */
9734         'evententer': true,
9735         /**
9736              * @event eventleave
9737              * Fires when the mouse leaves an
9738              * @param {Calendar} this
9739              * @param {event}
9740              */
9741         'eventleave': true,
9742         /**
9743              * @event eventclick
9744              * Fires when the mouse click an
9745              * @param {Calendar} this
9746              * @param {event}
9747              */
9748         'eventclick': true
9749         
9750     });
9751
9752 };
9753
9754 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9755     
9756      /**
9757      * @cfg {Number} startDay
9758      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9759      */
9760     startDay : 0,
9761     
9762     loadMask : false,
9763       
9764     getAutoCreate : function(){
9765         
9766         
9767         var fc_button = function(name, corner, style, content ) {
9768             return Roo.apply({},{
9769                 tag : 'span',
9770                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9771                          (corner.length ?
9772                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9773                             ''
9774                         ),
9775                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9776                 unselectable: 'on'
9777             });
9778         };
9779         
9780         var header = {
9781             tag : 'table',
9782             cls : 'fc-header',
9783             style : 'width:100%',
9784             cn : [
9785                 {
9786                     tag: 'tr',
9787                     cn : [
9788                         {
9789                             tag : 'td',
9790                             cls : 'fc-header-left',
9791                             cn : [
9792                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9793                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9794                                 { tag: 'span', cls: 'fc-header-space' },
9795                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9796                                 
9797                                 
9798                             ]
9799                         },
9800                         
9801                         {
9802                             tag : 'td',
9803                             cls : 'fc-header-center',
9804                             cn : [
9805                                 {
9806                                     tag: 'span',
9807                                     cls: 'fc-header-title',
9808                                     cn : {
9809                                         tag: 'H2',
9810                                         html : 'month / year'
9811                                     }
9812                                 }
9813                                 
9814                             ]
9815                         },
9816                         {
9817                             tag : 'td',
9818                             cls : 'fc-header-right',
9819                             cn : [
9820                           /*      fc_button('month', 'left', '', 'month' ),
9821                                 fc_button('week', '', '', 'week' ),
9822                                 fc_button('day', 'right', '', 'day' )
9823                             */    
9824                                 
9825                             ]
9826                         }
9827                         
9828                     ]
9829                 }
9830             ]
9831         };
9832         
9833        
9834         var cal_heads = function() {
9835             var ret = [];
9836             // fixme - handle this.
9837             
9838             for (var i =0; i < Date.dayNames.length; i++) {
9839                 var d = Date.dayNames[i];
9840                 ret.push({
9841                     tag: 'th',
9842                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9843                     html : d.substring(0,3)
9844                 });
9845                 
9846             }
9847             ret[0].cls += ' fc-first';
9848             ret[6].cls += ' fc-last';
9849             return ret;
9850         };
9851         var cal_cell = function(n) {
9852             return  {
9853                 tag: 'td',
9854                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9855                 cn : [
9856                     {
9857                         cn : [
9858                             {
9859                                 cls: 'fc-day-number',
9860                                 html: 'D'
9861                             },
9862                             {
9863                                 cls: 'fc-day-content',
9864                              
9865                                 cn : [
9866                                      {
9867                                         style: 'position: relative;' // height: 17px;
9868                                     }
9869                                 ]
9870                             }
9871                             
9872                             
9873                         ]
9874                     }
9875                 ]
9876                 
9877             }
9878         };
9879         var cal_rows = function() {
9880             
9881             var ret = []
9882             for (var r = 0; r < 6; r++) {
9883                 var row= {
9884                     tag : 'tr',
9885                     cls : 'fc-week',
9886                     cn : []
9887                 };
9888                 
9889                 for (var i =0; i < Date.dayNames.length; i++) {
9890                     var d = Date.dayNames[i];
9891                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9892
9893                 }
9894                 row.cn[0].cls+=' fc-first';
9895                 row.cn[0].cn[0].style = 'min-height:90px';
9896                 row.cn[6].cls+=' fc-last';
9897                 ret.push(row);
9898                 
9899             }
9900             ret[0].cls += ' fc-first';
9901             ret[4].cls += ' fc-prev-last';
9902             ret[5].cls += ' fc-last';
9903             return ret;
9904             
9905         };
9906         
9907         var cal_table = {
9908             tag: 'table',
9909             cls: 'fc-border-separate',
9910             style : 'width:100%',
9911             cellspacing  : 0,
9912             cn : [
9913                 { 
9914                     tag: 'thead',
9915                     cn : [
9916                         { 
9917                             tag: 'tr',
9918                             cls : 'fc-first fc-last',
9919                             cn : cal_heads()
9920                         }
9921                     ]
9922                 },
9923                 { 
9924                     tag: 'tbody',
9925                     cn : cal_rows()
9926                 }
9927                   
9928             ]
9929         };
9930          
9931          var cfg = {
9932             cls : 'fc fc-ltr',
9933             cn : [
9934                 header,
9935                 {
9936                     cls : 'fc-content',
9937                     style : "position: relative;",
9938                     cn : [
9939                         {
9940                             cls : 'fc-view fc-view-month fc-grid',
9941                             style : 'position: relative',
9942                             unselectable : 'on',
9943                             cn : [
9944                                 {
9945                                     cls : 'fc-event-container',
9946                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9947                                 },
9948                                 cal_table
9949                             ]
9950                         }
9951                     ]
9952     
9953                 }
9954            ] 
9955             
9956         };
9957         
9958          
9959         
9960         return cfg;
9961     },
9962     
9963     
9964     initEvents : function()
9965     {
9966         if(!this.store){
9967             throw "can not find store for calendar";
9968         }
9969         
9970         var mark = {
9971             tag: "div",
9972             cls:"x-dlg-mask",
9973             style: "text-align:center",
9974             cn: [
9975                 {
9976                     tag: "div",
9977                     style: "background-color:white;width:50%;margin:250 auto",
9978                     cn: [
9979                         {
9980                             tag: "img",
9981                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9982                         },
9983                         {
9984                             tag: "span",
9985                             html: "Loading"
9986                         }
9987                         
9988                     ]
9989                 }
9990             ]
9991         }
9992         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9993         
9994         var size = this.el.select('.fc-content', true).first().getSize();
9995         this.maskEl.setSize(size.width, size.height);
9996         this.maskEl.enableDisplayMode("block");
9997         if(!this.loadMask){
9998             this.maskEl.hide();
9999         }
10000         
10001         this.store = Roo.factory(this.store, Roo.data);
10002         this.store.on('load', this.onLoad, this);
10003         this.store.on('beforeload', this.onBeforeLoad, this);
10004         
10005         this.resize();
10006         
10007         this.cells = this.el.select('.fc-day',true);
10008         //Roo.log(this.cells);
10009         this.textNodes = this.el.query('.fc-day-number');
10010         this.cells.addClassOnOver('fc-state-hover');
10011         
10012         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10013         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10014         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10015         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10016         
10017         this.on('monthchange', this.onMonthChange, this);
10018         
10019         this.update(new Date().clearTime());
10020     },
10021     
10022     resize : function() {
10023         var sz  = this.el.getSize();
10024         
10025         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10026         this.el.select('.fc-day-content div',true).setHeight(34);
10027     },
10028     
10029     
10030     // private
10031     showPrevMonth : function(e){
10032         this.update(this.activeDate.add("mo", -1));
10033     },
10034     showToday : function(e){
10035         this.update(new Date().clearTime());
10036     },
10037     // private
10038     showNextMonth : function(e){
10039         this.update(this.activeDate.add("mo", 1));
10040     },
10041
10042     // private
10043     showPrevYear : function(){
10044         this.update(this.activeDate.add("y", -1));
10045     },
10046
10047     // private
10048     showNextYear : function(){
10049         this.update(this.activeDate.add("y", 1));
10050     },
10051
10052     
10053    // private
10054     update : function(date)
10055     {
10056         var vd = this.activeDate;
10057         this.activeDate = date;
10058 //        if(vd && this.el){
10059 //            var t = date.getTime();
10060 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10061 //                Roo.log('using add remove');
10062 //                
10063 //                this.fireEvent('monthchange', this, date);
10064 //                
10065 //                this.cells.removeClass("fc-state-highlight");
10066 //                this.cells.each(function(c){
10067 //                   if(c.dateValue == t){
10068 //                       c.addClass("fc-state-highlight");
10069 //                       setTimeout(function(){
10070 //                            try{c.dom.firstChild.focus();}catch(e){}
10071 //                       }, 50);
10072 //                       return false;
10073 //                   }
10074 //                   return true;
10075 //                });
10076 //                return;
10077 //            }
10078 //        }
10079         
10080         var days = date.getDaysInMonth();
10081         
10082         var firstOfMonth = date.getFirstDateOfMonth();
10083         var startingPos = firstOfMonth.getDay()-this.startDay;
10084         
10085         if(startingPos < this.startDay){
10086             startingPos += 7;
10087         }
10088         
10089         var pm = date.add(Date.MONTH, -1);
10090         var prevStart = pm.getDaysInMonth()-startingPos;
10091 //        
10092         this.cells = this.el.select('.fc-day',true);
10093         this.textNodes = this.el.query('.fc-day-number');
10094         this.cells.addClassOnOver('fc-state-hover');
10095         
10096         var cells = this.cells.elements;
10097         var textEls = this.textNodes;
10098         
10099         Roo.each(cells, function(cell){
10100             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10101         });
10102         
10103         days += startingPos;
10104
10105         // convert everything to numbers so it's fast
10106         var day = 86400000;
10107         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10108         //Roo.log(d);
10109         //Roo.log(pm);
10110         //Roo.log(prevStart);
10111         
10112         var today = new Date().clearTime().getTime();
10113         var sel = date.clearTime().getTime();
10114         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10115         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10116         var ddMatch = this.disabledDatesRE;
10117         var ddText = this.disabledDatesText;
10118         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10119         var ddaysText = this.disabledDaysText;
10120         var format = this.format;
10121         
10122         var setCellClass = function(cal, cell){
10123             
10124             //Roo.log('set Cell Class');
10125             cell.title = "";
10126             var t = d.getTime();
10127             
10128             //Roo.log(d);
10129             
10130             cell.dateValue = t;
10131             if(t == today){
10132                 cell.className += " fc-today";
10133                 cell.className += " fc-state-highlight";
10134                 cell.title = cal.todayText;
10135             }
10136             if(t == sel){
10137                 // disable highlight in other month..
10138                 //cell.className += " fc-state-highlight";
10139                 
10140             }
10141             // disabling
10142             if(t < min) {
10143                 cell.className = " fc-state-disabled";
10144                 cell.title = cal.minText;
10145                 return;
10146             }
10147             if(t > max) {
10148                 cell.className = " fc-state-disabled";
10149                 cell.title = cal.maxText;
10150                 return;
10151             }
10152             if(ddays){
10153                 if(ddays.indexOf(d.getDay()) != -1){
10154                     cell.title = ddaysText;
10155                     cell.className = " fc-state-disabled";
10156                 }
10157             }
10158             if(ddMatch && format){
10159                 var fvalue = d.dateFormat(format);
10160                 if(ddMatch.test(fvalue)){
10161                     cell.title = ddText.replace("%0", fvalue);
10162                     cell.className = " fc-state-disabled";
10163                 }
10164             }
10165             
10166             if (!cell.initialClassName) {
10167                 cell.initialClassName = cell.dom.className;
10168             }
10169             
10170             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10171         };
10172
10173         var i = 0;
10174         
10175         for(; i < startingPos; i++) {
10176             textEls[i].innerHTML = (++prevStart);
10177             d.setDate(d.getDate()+1);
10178             
10179             cells[i].className = "fc-past fc-other-month";
10180             setCellClass(this, cells[i]);
10181         }
10182         
10183         var intDay = 0;
10184         
10185         for(; i < days; i++){
10186             intDay = i - startingPos + 1;
10187             textEls[i].innerHTML = (intDay);
10188             d.setDate(d.getDate()+1);
10189             
10190             cells[i].className = ''; // "x-date-active";
10191             setCellClass(this, cells[i]);
10192         }
10193         var extraDays = 0;
10194         
10195         for(; i < 42; i++) {
10196             textEls[i].innerHTML = (++extraDays);
10197             d.setDate(d.getDate()+1);
10198             
10199             cells[i].className = "fc-future fc-other-month";
10200             setCellClass(this, cells[i]);
10201         }
10202         
10203         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10204         
10205         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10206         
10207         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10208         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10209         
10210         if(totalRows != 6){
10211             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10212             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10213         }
10214         
10215         this.fireEvent('monthchange', this, date);
10216         
10217         
10218         /*
10219         if(!this.internalRender){
10220             var main = this.el.dom.firstChild;
10221             var w = main.offsetWidth;
10222             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10223             Roo.fly(main).setWidth(w);
10224             this.internalRender = true;
10225             // opera does not respect the auto grow header center column
10226             // then, after it gets a width opera refuses to recalculate
10227             // without a second pass
10228             if(Roo.isOpera && !this.secondPass){
10229                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10230                 this.secondPass = true;
10231                 this.update.defer(10, this, [date]);
10232             }
10233         }
10234         */
10235         
10236     },
10237     
10238     findCell : function(dt) {
10239         dt = dt.clearTime().getTime();
10240         var ret = false;
10241         this.cells.each(function(c){
10242             //Roo.log("check " +c.dateValue + '?=' + dt);
10243             if(c.dateValue == dt){
10244                 ret = c;
10245                 return false;
10246             }
10247             return true;
10248         });
10249         
10250         return ret;
10251     },
10252     
10253     findCells : function(ev) {
10254         var s = ev.start.clone().clearTime().getTime();
10255        // Roo.log(s);
10256         var e= ev.end.clone().clearTime().getTime();
10257        // Roo.log(e);
10258         var ret = [];
10259         this.cells.each(function(c){
10260              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10261             
10262             if(c.dateValue > e){
10263                 return ;
10264             }
10265             if(c.dateValue < s){
10266                 return ;
10267             }
10268             ret.push(c);
10269         });
10270         
10271         return ret;    
10272     },
10273     
10274     findBestRow: function(cells)
10275     {
10276         var ret = 0;
10277         
10278         for (var i =0 ; i < cells.length;i++) {
10279             ret  = Math.max(cells[i].rows || 0,ret);
10280         }
10281         return ret;
10282         
10283     },
10284     
10285     
10286     addItem : function(ev)
10287     {
10288         // look for vertical location slot in
10289         var cells = this.findCells(ev);
10290         
10291         ev.row = this.findBestRow(cells);
10292         
10293         // work out the location.
10294         
10295         var crow = false;
10296         var rows = [];
10297         for(var i =0; i < cells.length; i++) {
10298             if (!crow) {
10299                 crow = {
10300                     start : cells[i],
10301                     end :  cells[i]
10302                 };
10303                 continue;
10304             }
10305             if (crow.start.getY() == cells[i].getY()) {
10306                 // on same row.
10307                 crow.end = cells[i];
10308                 continue;
10309             }
10310             // different row.
10311             rows.push(crow);
10312             crow = {
10313                 start: cells[i],
10314                 end : cells[i]
10315             };
10316             
10317         }
10318         
10319         rows.push(crow);
10320         ev.els = [];
10321         ev.rows = rows;
10322         ev.cells = cells;
10323         for (var i = 0; i < cells.length;i++) {
10324             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10325             
10326         }
10327         
10328         this.calevents.push(ev);
10329     },
10330     
10331     clearEvents: function() {
10332         
10333         if(!this.calevents){
10334             return;
10335         }
10336         
10337         Roo.each(this.cells.elements, function(c){
10338             c.rows = 0;
10339         });
10340         
10341         Roo.each(this.calevents, function(e) {
10342             Roo.each(e.els, function(el) {
10343                 el.un('mouseenter' ,this.onEventEnter, this);
10344                 el.un('mouseleave' ,this.onEventLeave, this);
10345                 el.remove();
10346             },this);
10347         },this);
10348         
10349     },
10350     
10351     renderEvents: function()
10352     {   
10353         // first make sure there is enough space..
10354         
10355         this.cells.each(function(c) {
10356         
10357             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10358         });
10359         
10360         for (var e = 0; e < this.calevents.length; e++) {
10361             var ev = this.calevents[e];
10362             var cells = ev.cells;
10363             var rows = ev.rows;
10364             
10365             for(var i =0; i < rows.length; i++) {
10366                 
10367                  
10368                 // how many rows should it span..
10369                 
10370                 var  cfg = {
10371                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10372                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10373                     
10374                     unselectable : "on",
10375                     cn : [
10376                         {
10377                             cls: 'fc-event-inner',
10378                             cn : [
10379 //                                {
10380 //                                  tag:'span',
10381 //                                  cls: 'fc-event-time',
10382 //                                  html : cells.length > 1 ? '' : ev.time
10383 //                                },
10384                                 {
10385                                   tag:'span',
10386                                   cls: 'fc-event-title',
10387                                   html : String.format('{0}', ev.title)
10388                                 }
10389                                 
10390                                 
10391                             ]
10392                         },
10393                         {
10394                             cls: 'ui-resizable-handle ui-resizable-e',
10395                             html : '&nbsp;&nbsp;&nbsp'
10396                         }
10397                         
10398                     ]
10399                 };
10400                 if (i == 0) {
10401                     cfg.cls += ' fc-event-start';
10402                 }
10403                 if ((i+1) == rows.length) {
10404                     cfg.cls += ' fc-event-end';
10405                 }
10406                 
10407                 var ctr = this.el.select('.fc-event-container',true).first();
10408                 var cg = ctr.createChild(cfg);
10409                 
10410                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10411                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10412                 cg.on('click', this.onEventClick, this, ev);
10413                 
10414                 ev.els.push(cg);
10415                 
10416                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10417                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10418                 //Roo.log(cg);
10419                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10420                 cg.setWidth(ebox.right - sbox.x -2);
10421             }
10422             
10423             
10424         }
10425         
10426     },
10427     
10428     onEventEnter: function (e, el,event,d) {
10429         this.fireEvent('evententer', this, el, event);
10430     },
10431     
10432     onEventLeave: function (e, el,event,d) {
10433         this.fireEvent('eventleave', this, el, event);
10434     },
10435     
10436     onEventClick: function (e, el,event,d) {
10437         this.fireEvent('eventclick', this, el, event);
10438     },
10439     
10440     onMonthChange: function () {
10441         this.store.load();
10442     },
10443     
10444     onLoad: function () 
10445     {   
10446         this.calevents = [];
10447         var cal = this;
10448         
10449         if(this.store.getCount() > 0){
10450             this.store.data.each(function(d){
10451                cal.addItem({
10452                     id : d.data.id,
10453                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10454                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10455                     time : d.data.start_time,
10456                     title : d.data.title,
10457                     description : d.data.description,
10458                     venue : d.data.venue
10459                 });
10460             });
10461         }
10462         
10463         this.renderEvents();
10464         
10465         if(this.loadMask){
10466             this.maskEl.hide();
10467         }
10468     },
10469     
10470     onBeforeLoad: function()
10471     {
10472         this.clearEvents();
10473         
10474         if(this.loadMask){
10475             this.maskEl.show();
10476         }
10477     }
10478 });
10479
10480  
10481  /*
10482  * - LGPL
10483  *
10484  * element
10485  * 
10486  */
10487
10488 /**
10489  * @class Roo.bootstrap.Popover
10490  * @extends Roo.bootstrap.Component
10491  * Bootstrap Popover class
10492  * @cfg {String} html contents of the popover   (or false to use children..)
10493  * @cfg {String} title of popover (or false to hide)
10494  * @cfg {String} placement how it is placed
10495  * @cfg {String} trigger click || hover (or false to trigger manually)
10496  * @cfg {String} over what (parent or false to trigger manually.)
10497  * 
10498  * @constructor
10499  * Create a new Popover
10500  * @param {Object} config The config object
10501  */
10502
10503 Roo.bootstrap.Popover = function(config){
10504     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10505 };
10506
10507 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10508     
10509     title: 'Fill in a title',
10510     html: false,
10511     
10512     placement : 'right',
10513     trigger : 'hover', // hover
10514     
10515     over: 'parent',
10516     
10517     can_build_overlaid : false,
10518     
10519     getChildContainer : function()
10520     {
10521         return this.el.select('.popover-content',true).first();
10522     },
10523     
10524     getAutoCreate : function(){
10525          Roo.log('make popover?');
10526         var cfg = {
10527            cls : 'popover roo-dynamic',
10528            style: 'display:block',
10529            cn : [
10530                 {
10531                     cls : 'arrow'
10532                 },
10533                 {
10534                     cls : 'popover-inner',
10535                     cn : [
10536                         {
10537                             tag: 'h3',
10538                             cls: 'popover-title',
10539                             html : this.title
10540                         },
10541                         {
10542                             cls : 'popover-content',
10543                             html : this.html
10544                         }
10545                     ]
10546                     
10547                 }
10548            ]
10549         };
10550         
10551         return cfg;
10552     },
10553     setTitle: function(str)
10554     {
10555         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10556     },
10557     setContent: function(str)
10558     {
10559         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10560     },
10561     // as it get's added to the bottom of the page.
10562     onRender : function(ct, position)
10563     {
10564         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10565         if(!this.el){
10566             var cfg = Roo.apply({},  this.getAutoCreate());
10567             cfg.id = Roo.id();
10568             
10569             if (this.cls) {
10570                 cfg.cls += ' ' + this.cls;
10571             }
10572             if (this.style) {
10573                 cfg.style = this.style;
10574             }
10575             Roo.log("adding to ")
10576             this.el = Roo.get(document.body).createChild(cfg, position);
10577             Roo.log(this.el);
10578         }
10579         this.initEvents();
10580     },
10581     
10582     initEvents : function()
10583     {
10584         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10585         this.el.enableDisplayMode('block');
10586         this.el.hide();
10587         if (this.over === false) {
10588             return; 
10589         }
10590         if (this.triggers === false) {
10591             return;
10592         }
10593         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10594         var triggers = this.trigger ? this.trigger.split(' ') : [];
10595         Roo.each(triggers, function(trigger) {
10596         
10597             if (trigger == 'click') {
10598                 on_el.on('click', this.toggle, this);
10599             } else if (trigger != 'manual') {
10600                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10601                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10602       
10603                 on_el.on(eventIn  ,this.enter, this);
10604                 on_el.on(eventOut, this.leave, this);
10605             }
10606         }, this);
10607         
10608     },
10609     
10610     
10611     // private
10612     timeout : null,
10613     hoverState : null,
10614     
10615     toggle : function () {
10616         this.hoverState == 'in' ? this.leave() : this.enter();
10617     },
10618     
10619     enter : function () {
10620        
10621     
10622         clearTimeout(this.timeout);
10623     
10624         this.hoverState = 'in'
10625     
10626         if (!this.delay || !this.delay.show) {
10627             this.show();
10628             return 
10629         }
10630         var _t = this;
10631         this.timeout = setTimeout(function () {
10632             if (_t.hoverState == 'in') {
10633                 _t.show();
10634             }
10635         }, this.delay.show)
10636     },
10637     leave : function() {
10638         clearTimeout(this.timeout);
10639     
10640         this.hoverState = 'out'
10641     
10642         if (!this.delay || !this.delay.hide) {
10643             this.hide();
10644             return 
10645         }
10646         var _t = this;
10647         this.timeout = setTimeout(function () {
10648             if (_t.hoverState == 'out') {
10649                 _t.hide();
10650             }
10651         }, this.delay.hide)
10652     },
10653     
10654     show : function (on_el)
10655     {
10656         if (!on_el) {
10657             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10658         }
10659         // set content.
10660         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10661         if (this.html !== false) {
10662             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10663         }
10664         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10665         if (!this.title.length) {
10666             this.el.select('.popover-title',true).hide();
10667         }
10668         
10669         var placement = typeof this.placement == 'function' ?
10670             this.placement.call(this, this.el, on_el) :
10671             this.placement;
10672             
10673         var autoToken = /\s?auto?\s?/i;
10674         var autoPlace = autoToken.test(placement);
10675         if (autoPlace) {
10676             placement = placement.replace(autoToken, '') || 'top';
10677         }
10678         
10679         //this.el.detach()
10680         //this.el.setXY([0,0]);
10681         this.el.show();
10682         this.el.dom.style.display='block';
10683         this.el.addClass(placement);
10684         
10685         //this.el.appendTo(on_el);
10686         
10687         var p = this.getPosition();
10688         var box = this.el.getBox();
10689         
10690         if (autoPlace) {
10691             // fixme..
10692         }
10693         var align = Roo.bootstrap.Popover.alignment[placement]
10694         this.el.alignTo(on_el, align[0],align[1]);
10695         //var arrow = this.el.select('.arrow',true).first();
10696         //arrow.set(align[2], 
10697         
10698         this.el.addClass('in');
10699         this.hoverState = null;
10700         
10701         if (this.el.hasClass('fade')) {
10702             // fade it?
10703         }
10704         
10705     },
10706     hide : function()
10707     {
10708         this.el.setXY([0,0]);
10709         this.el.removeClass('in');
10710         this.el.hide();
10711         
10712     }
10713     
10714 });
10715
10716 Roo.bootstrap.Popover.alignment = {
10717     'left' : ['r-l', [-10,0], 'right'],
10718     'right' : ['l-r', [10,0], 'left'],
10719     'bottom' : ['t-b', [0,10], 'top'],
10720     'top' : [ 'b-t', [0,-10], 'bottom']
10721 };
10722
10723  /*
10724  * - LGPL
10725  *
10726  * Progress
10727  * 
10728  */
10729
10730 /**
10731  * @class Roo.bootstrap.Progress
10732  * @extends Roo.bootstrap.Component
10733  * Bootstrap Progress class
10734  * @cfg {Boolean} striped striped of the progress bar
10735  * @cfg {Boolean} active animated of the progress bar
10736  * 
10737  * 
10738  * @constructor
10739  * Create a new Progress
10740  * @param {Object} config The config object
10741  */
10742
10743 Roo.bootstrap.Progress = function(config){
10744     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10745 };
10746
10747 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10748     
10749     striped : false,
10750     active: false,
10751     
10752     getAutoCreate : function(){
10753         var cfg = {
10754             tag: 'div',
10755             cls: 'progress'
10756         };
10757         
10758         
10759         if(this.striped){
10760             cfg.cls += ' progress-striped';
10761         }
10762       
10763         if(this.active){
10764             cfg.cls += ' active';
10765         }
10766         
10767         
10768         return cfg;
10769     }
10770    
10771 });
10772
10773  
10774
10775  /*
10776  * - LGPL
10777  *
10778  * ProgressBar
10779  * 
10780  */
10781
10782 /**
10783  * @class Roo.bootstrap.ProgressBar
10784  * @extends Roo.bootstrap.Component
10785  * Bootstrap ProgressBar class
10786  * @cfg {Number} aria_valuenow aria-value now
10787  * @cfg {Number} aria_valuemin aria-value min
10788  * @cfg {Number} aria_valuemax aria-value max
10789  * @cfg {String} label label for the progress bar
10790  * @cfg {String} panel (success | info | warning | danger )
10791  * @cfg {String} role role of the progress bar
10792  * @cfg {String} sr_only text
10793  * 
10794  * 
10795  * @constructor
10796  * Create a new ProgressBar
10797  * @param {Object} config The config object
10798  */
10799
10800 Roo.bootstrap.ProgressBar = function(config){
10801     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10802 };
10803
10804 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10805     
10806     aria_valuenow : 0,
10807     aria_valuemin : 0,
10808     aria_valuemax : 100,
10809     label : false,
10810     panel : false,
10811     role : false,
10812     sr_only: false,
10813     
10814     getAutoCreate : function()
10815     {
10816         
10817         var cfg = {
10818             tag: 'div',
10819             cls: 'progress-bar',
10820             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10821         };
10822         
10823         if(this.sr_only){
10824             cfg.cn = {
10825                 tag: 'span',
10826                 cls: 'sr-only',
10827                 html: this.sr_only
10828             }
10829         }
10830         
10831         if(this.role){
10832             cfg.role = this.role;
10833         }
10834         
10835         if(this.aria_valuenow){
10836             cfg['aria-valuenow'] = this.aria_valuenow;
10837         }
10838         
10839         if(this.aria_valuemin){
10840             cfg['aria-valuemin'] = this.aria_valuemin;
10841         }
10842         
10843         if(this.aria_valuemax){
10844             cfg['aria-valuemax'] = this.aria_valuemax;
10845         }
10846         
10847         if(this.label && !this.sr_only){
10848             cfg.html = this.label;
10849         }
10850         
10851         if(this.panel){
10852             cfg.cls += ' progress-bar-' + this.panel;
10853         }
10854         
10855         return cfg;
10856     },
10857     
10858     update : function(aria_valuenow)
10859     {
10860         this.aria_valuenow = aria_valuenow;
10861         
10862         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10863     }
10864    
10865 });
10866
10867  
10868
10869  /*
10870  * - LGPL
10871  *
10872  * TabPanel
10873  * 
10874  */
10875
10876 /**
10877  * @class Roo.bootstrap.TabPanel
10878  * @extends Roo.bootstrap.Component
10879  * Bootstrap TabPanel class
10880  * @cfg {Boolean} active panel active
10881  * @cfg {String} html panel content
10882  * @cfg {String} tabId tab relate id
10883  * 
10884  * 
10885  * @constructor
10886  * Create a new TabPanel
10887  * @param {Object} config The config object
10888  */
10889
10890 Roo.bootstrap.TabPanel = function(config){
10891     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10892 };
10893
10894 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10895     
10896     active: false,
10897     html: false,
10898     tabId: false,
10899     
10900     getAutoCreate : function(){
10901         var cfg = {
10902             tag: 'div',
10903             cls: 'tab-pane',
10904             html: this.html || ''
10905         };
10906         
10907         if(this.active){
10908             cfg.cls += ' active';
10909         }
10910         
10911         if(this.tabId){
10912             cfg.tabId = this.tabId;
10913         }
10914         
10915         return cfg;
10916     }
10917    
10918 });
10919
10920  
10921
10922  /*
10923  * - LGPL
10924  *
10925  * DateField
10926  * 
10927  */
10928
10929 /**
10930  * @class Roo.bootstrap.DateField
10931  * @extends Roo.bootstrap.Input
10932  * Bootstrap DateField class
10933  * @cfg {Number} weekStart default 0
10934  * @cfg {Number} weekStart default 0
10935  * @cfg {Number} viewMode default empty, (months|years)
10936  * @cfg {Number} minViewMode default empty, (months|years)
10937  * @cfg {Number} startDate default -Infinity
10938  * @cfg {Number} endDate default Infinity
10939  * @cfg {Boolean} todayHighlight default false
10940  * @cfg {Boolean} todayBtn default false
10941  * @cfg {Boolean} calendarWeeks default false
10942  * @cfg {Object} daysOfWeekDisabled default empty
10943  * 
10944  * @cfg {Boolean} keyboardNavigation default true
10945  * @cfg {String} language default en
10946  * 
10947  * @constructor
10948  * Create a new DateField
10949  * @param {Object} config The config object
10950  */
10951
10952 Roo.bootstrap.DateField = function(config){
10953     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10954      this.addEvents({
10955             /**
10956              * @event show
10957              * Fires when this field show.
10958              * @param {Roo.bootstrap.DateField} this
10959              * @param {Mixed} date The date value
10960              */
10961             show : true,
10962             /**
10963              * @event show
10964              * Fires when this field hide.
10965              * @param {Roo.bootstrap.DateField} this
10966              * @param {Mixed} date The date value
10967              */
10968             hide : true,
10969             /**
10970              * @event select
10971              * Fires when select a date.
10972              * @param {Roo.bootstrap.DateField} this
10973              * @param {Mixed} date The date value
10974              */
10975             select : true
10976         });
10977 };
10978
10979 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10980     
10981     /**
10982      * @cfg {String} format
10983      * The default date format string which can be overriden for localization support.  The format must be
10984      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10985      */
10986     format : "m/d/y",
10987     /**
10988      * @cfg {String} altFormats
10989      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10990      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10991      */
10992     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10993     
10994     weekStart : 0,
10995     
10996     viewMode : '',
10997     
10998     minViewMode : '',
10999     
11000     todayHighlight : false,
11001     
11002     todayBtn: false,
11003     
11004     language: 'en',
11005     
11006     keyboardNavigation: true,
11007     
11008     calendarWeeks: false,
11009     
11010     startDate: -Infinity,
11011     
11012     endDate: Infinity,
11013     
11014     daysOfWeekDisabled: [],
11015     
11016     _events: [],
11017     
11018     UTCDate: function()
11019     {
11020         return new Date(Date.UTC.apply(Date, arguments));
11021     },
11022     
11023     UTCToday: function()
11024     {
11025         var today = new Date();
11026         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11027     },
11028     
11029     getDate: function() {
11030             var d = this.getUTCDate();
11031             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11032     },
11033     
11034     getUTCDate: function() {
11035             return this.date;
11036     },
11037     
11038     setDate: function(d) {
11039             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11040     },
11041     
11042     setUTCDate: function(d) {
11043             this.date = d;
11044             this.setValue(this.formatDate(this.date));
11045     },
11046         
11047     onRender: function(ct, position)
11048     {
11049         
11050         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11051         
11052         this.language = this.language || 'en';
11053         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11054         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11055         
11056         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11057         this.format = this.format || 'm/d/y';
11058         this.isInline = false;
11059         this.isInput = true;
11060         this.component = this.el.select('.add-on', true).first() || false;
11061         this.component = (this.component && this.component.length === 0) ? false : this.component;
11062         this.hasInput = this.component && this.inputEL().length;
11063         
11064         if (typeof(this.minViewMode === 'string')) {
11065             switch (this.minViewMode) {
11066                 case 'months':
11067                     this.minViewMode = 1;
11068                     break;
11069                 case 'years':
11070                     this.minViewMode = 2;
11071                     break;
11072                 default:
11073                     this.minViewMode = 0;
11074                     break;
11075             }
11076         }
11077         
11078         if (typeof(this.viewMode === 'string')) {
11079             switch (this.viewMode) {
11080                 case 'months':
11081                     this.viewMode = 1;
11082                     break;
11083                 case 'years':
11084                     this.viewMode = 2;
11085                     break;
11086                 default:
11087                     this.viewMode = 0;
11088                     break;
11089             }
11090         }
11091                 
11092         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11093         
11094         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11095         
11096         this.picker().on('mousedown', this.onMousedown, this);
11097         this.picker().on('click', this.onClick, this);
11098         
11099         this.picker().addClass('datepicker-dropdown');
11100         
11101         this.startViewMode = this.viewMode;
11102         
11103         
11104         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11105             if(!this.calendarWeeks){
11106                 v.remove();
11107                 return;
11108             };
11109             
11110             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11111             v.attr('colspan', function(i, val){
11112                 return parseInt(val) + 1;
11113             });
11114         })
11115                         
11116         
11117         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11118         
11119         this.setStartDate(this.startDate);
11120         this.setEndDate(this.endDate);
11121         
11122         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11123         
11124         this.fillDow();
11125         this.fillMonths();
11126         this.update();
11127         this.showMode();
11128         
11129         if(this.isInline) {
11130             this.show();
11131         }
11132     },
11133     
11134     picker : function()
11135     {
11136         return this.el.select('.datepicker', true).first();
11137     },
11138     
11139     fillDow: function()
11140     {
11141         var dowCnt = this.weekStart;
11142         
11143         var dow = {
11144             tag: 'tr',
11145             cn: [
11146                 
11147             ]
11148         };
11149         
11150         if(this.calendarWeeks){
11151             dow.cn.push({
11152                 tag: 'th',
11153                 cls: 'cw',
11154                 html: '&nbsp;'
11155             })
11156         }
11157         
11158         while (dowCnt < this.weekStart + 7) {
11159             dow.cn.push({
11160                 tag: 'th',
11161                 cls: 'dow',
11162                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11163             });
11164         }
11165         
11166         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11167     },
11168     
11169     fillMonths: function()
11170     {    
11171         var i = 0
11172         var months = this.picker().select('>.datepicker-months td', true).first();
11173         
11174         months.dom.innerHTML = '';
11175         
11176         while (i < 12) {
11177             var month = {
11178                 tag: 'span',
11179                 cls: 'month',
11180                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11181             }
11182             
11183             months.createChild(month);
11184         }
11185         
11186     },
11187     
11188     update: function(){
11189         
11190         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11191         
11192         if (this.date < this.startDate) {
11193             this.viewDate = new Date(this.startDate);
11194         } else if (this.date > this.endDate) {
11195             this.viewDate = new Date(this.endDate);
11196         } else {
11197             this.viewDate = new Date(this.date);
11198         }
11199         
11200         this.fill();
11201     },
11202     
11203     fill: function() {
11204         var d = new Date(this.viewDate),
11205                 year = d.getUTCFullYear(),
11206                 month = d.getUTCMonth(),
11207                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11208                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11209                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11210                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11211                 currentDate = this.date && this.date.valueOf(),
11212                 today = this.UTCToday();
11213         
11214         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11215         
11216 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11217         
11218 //        this.picker.select('>tfoot th.today').
11219 //                                              .text(dates[this.language].today)
11220 //                                              .toggle(this.todayBtn !== false);
11221     
11222         this.updateNavArrows();
11223         this.fillMonths();
11224                                                 
11225         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11226         
11227         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11228          
11229         prevMonth.setUTCDate(day);
11230         
11231         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11232         
11233         var nextMonth = new Date(prevMonth);
11234         
11235         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11236         
11237         nextMonth = nextMonth.valueOf();
11238         
11239         var fillMonths = false;
11240         
11241         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11242         
11243         while(prevMonth.valueOf() < nextMonth) {
11244             var clsName = '';
11245             
11246             if (prevMonth.getUTCDay() === this.weekStart) {
11247                 if(fillMonths){
11248                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11249                 }
11250                     
11251                 fillMonths = {
11252                     tag: 'tr',
11253                     cn: []
11254                 };
11255                 
11256                 if(this.calendarWeeks){
11257                     // ISO 8601: First week contains first thursday.
11258                     // ISO also states week starts on Monday, but we can be more abstract here.
11259                     var
11260                     // Start of current week: based on weekstart/current date
11261                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11262                     // Thursday of this week
11263                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11264                     // First Thursday of year, year from thursday
11265                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11266                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11267                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11268                     
11269                     fillMonths.cn.push({
11270                         tag: 'td',
11271                         cls: 'cw',
11272                         html: calWeek
11273                     });
11274                 }
11275             }
11276             
11277             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11278                 clsName += ' old';
11279             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11280                 clsName += ' new';
11281             }
11282             if (this.todayHighlight &&
11283                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11284                 prevMonth.getUTCMonth() == today.getMonth() &&
11285                 prevMonth.getUTCDate() == today.getDate()) {
11286                 clsName += ' today';
11287             }
11288             
11289             if (currentDate && prevMonth.valueOf() === currentDate) {
11290                 clsName += ' active';
11291             }
11292             
11293             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11294                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11295                     clsName += ' disabled';
11296             }
11297             
11298             fillMonths.cn.push({
11299                 tag: 'td',
11300                 cls: 'day ' + clsName,
11301                 html: prevMonth.getDate()
11302             })
11303             
11304             prevMonth.setDate(prevMonth.getDate()+1);
11305         }
11306           
11307         var currentYear = this.date && this.date.getUTCFullYear();
11308         var currentMonth = this.date && this.date.getUTCMonth();
11309         
11310         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11311         
11312         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11313             v.removeClass('active');
11314             
11315             if(currentYear === year && k === currentMonth){
11316                 v.addClass('active');
11317             }
11318             
11319             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11320                 v.addClass('disabled');
11321             }
11322             
11323         });
11324         
11325         
11326         year = parseInt(year/10, 10) * 10;
11327         
11328         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11329         
11330         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11331         
11332         year -= 1;
11333         for (var i = -1; i < 11; i++) {
11334             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11335                 tag: 'span',
11336                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11337                 html: year
11338             })
11339             
11340             year += 1;
11341         }
11342     },
11343     
11344     showMode: function(dir) {
11345         if (dir) {
11346             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11347         }
11348         Roo.each(this.picker().select('>div',true).elements, function(v){
11349             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11350             v.hide();
11351         });
11352         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11353     },
11354     
11355     place: function()
11356     {
11357         if(this.isInline) return;
11358         
11359         this.picker().removeClass(['bottom', 'top']);
11360         
11361         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11362             /*
11363              * place to the top of element!
11364              *
11365              */
11366             
11367             this.picker().addClass('top');
11368             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11369             
11370             return;
11371         }
11372         
11373         this.picker().addClass('bottom');
11374         
11375         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11376     },
11377     
11378     parseDate : function(value){
11379         if(!value || value instanceof Date){
11380             return value;
11381         }
11382         var v = Date.parseDate(value, this.format);
11383         if (!v && this.useIso) {
11384             v = Date.parseDate(value, 'Y-m-d');
11385         }
11386         if(!v && this.altFormats){
11387             if(!this.altFormatsArray){
11388                 this.altFormatsArray = this.altFormats.split("|");
11389             }
11390             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11391                 v = Date.parseDate(value, this.altFormatsArray[i]);
11392             }
11393         }
11394         return v;
11395     },
11396     
11397     formatDate : function(date, fmt){
11398         return (!date || !(date instanceof Date)) ?
11399         date : date.dateFormat(fmt || this.format);
11400     },
11401     
11402     onFocus : function()
11403     {
11404         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11405         this.show();
11406     },
11407     
11408     onBlur : function()
11409     {
11410         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11411         this.hide();
11412     },
11413     
11414     show : function()
11415     {
11416         this.picker().show();
11417         this.update();
11418         this.place();
11419         
11420         this.fireEvent('show', this, this.date);
11421     },
11422     
11423     hide : function()
11424     {
11425         if(this.isInline) return;
11426         this.picker().hide();
11427         this.viewMode = this.startViewMode;
11428         this.showMode();
11429         
11430         this.fireEvent('hide', this, this.date);
11431         
11432     },
11433     
11434     onMousedown: function(e){
11435         e.stopPropagation();
11436         e.preventDefault();
11437     },
11438     
11439     keyup: function(e){
11440         Roo.bootstrap.DateField.superclass.keyup.call(this);
11441         this.update();
11442         
11443     },
11444
11445     setValue: function(v){
11446         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11447         
11448         this.fireEvent('select', this, this.date);
11449         
11450     },
11451     
11452     fireKey: function(e){
11453         if (!this.picker().isVisible()){
11454             if (e.keyCode == 27) // allow escape to hide and re-show picker
11455                 this.show();
11456             return;
11457         }
11458         var dateChanged = false,
11459         dir, day, month,
11460         newDate, newViewDate;
11461         switch(e.keyCode){
11462             case 27: // escape
11463                 this.hide();
11464                 e.preventDefault();
11465                 break;
11466             case 37: // left
11467             case 39: // right
11468                 if (!this.keyboardNavigation) break;
11469                 dir = e.keyCode == 37 ? -1 : 1;
11470                 
11471                 if (e.ctrlKey){
11472                     newDate = this.moveYear(this.date, dir);
11473                     newViewDate = this.moveYear(this.viewDate, dir);
11474                 } else if (e.shiftKey){
11475                     newDate = this.moveMonth(this.date, dir);
11476                     newViewDate = this.moveMonth(this.viewDate, dir);
11477                 } else {
11478                     newDate = new Date(this.date);
11479                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11480                     newViewDate = new Date(this.viewDate);
11481                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11482                 }
11483                 if (this.dateWithinRange(newDate)){
11484                     this.date = newDate;
11485                     this.viewDate = newViewDate;
11486                     this.setValue(this.formatDate(this.date));
11487                     this.update();
11488                     e.preventDefault();
11489                     dateChanged = true;
11490                 }
11491                 break;
11492             case 38: // up
11493             case 40: // down
11494                 if (!this.keyboardNavigation) break;
11495                 dir = e.keyCode == 38 ? -1 : 1;
11496                 if (e.ctrlKey){
11497                     newDate = this.moveYear(this.date, dir);
11498                     newViewDate = this.moveYear(this.viewDate, dir);
11499                 } else if (e.shiftKey){
11500                     newDate = this.moveMonth(this.date, dir);
11501                     newViewDate = this.moveMonth(this.viewDate, dir);
11502                 } else {
11503                     newDate = new Date(this.date);
11504                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11505                     newViewDate = new Date(this.viewDate);
11506                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11507                 }
11508                 if (this.dateWithinRange(newDate)){
11509                     this.date = newDate;
11510                     this.viewDate = newViewDate;
11511                     this.setValue(this.formatDate(this.date));
11512                     this.update();
11513                     e.preventDefault();
11514                     dateChanged = true;
11515                 }
11516                 break;
11517             case 13: // enter
11518                 this.setValue(this.formatDate(this.date));
11519                 this.hide();
11520                 e.preventDefault();
11521                 break;
11522             case 9: // tab
11523                 this.setValue(this.formatDate(this.date));
11524                 this.hide();
11525                 break;
11526         }
11527     },
11528     
11529     
11530     onClick: function(e) {
11531         e.stopPropagation();
11532         e.preventDefault();
11533         
11534         var target = e.getTarget();
11535         
11536         if(target.nodeName.toLowerCase() === 'i'){
11537             target = Roo.get(target).dom.parentNode;
11538         }
11539         
11540         var nodeName = target.nodeName;
11541         var className = target.className;
11542         var html = target.innerHTML;
11543         
11544         switch(nodeName.toLowerCase()) {
11545             case 'th':
11546                 switch(className) {
11547                     case 'switch':
11548                         this.showMode(1);
11549                         break;
11550                     case 'prev':
11551                     case 'next':
11552                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11553                         switch(this.viewMode){
11554                                 case 0:
11555                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11556                                         break;
11557                                 case 1:
11558                                 case 2:
11559                                         this.viewDate = this.moveYear(this.viewDate, dir);
11560                                         break;
11561                         }
11562                         this.fill();
11563                         break;
11564                     case 'today':
11565                         var date = new Date();
11566                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11567                         this.fill()
11568                         this.setValue(this.formatDate(this.date));
11569                         this.hide();
11570                         break;
11571                 }
11572                 break;
11573             case 'span':
11574                 if (className.indexOf('disabled') === -1) {
11575                     this.viewDate.setUTCDate(1);
11576                     if (className.indexOf('month') !== -1) {
11577                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11578                     } else {
11579                         var year = parseInt(html, 10) || 0;
11580                         this.viewDate.setUTCFullYear(year);
11581                         
11582                     }
11583                     this.showMode(-1);
11584                     this.fill();
11585                 }
11586                 break;
11587                 
11588             case 'td':
11589                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11590                     var day = parseInt(html, 10) || 1;
11591                     var year = this.viewDate.getUTCFullYear(),
11592                         month = this.viewDate.getUTCMonth();
11593
11594                     if (className.indexOf('old') !== -1) {
11595                         if(month === 0 ){
11596                             month = 11;
11597                             year -= 1;
11598                         }else{
11599                             month -= 1;
11600                         }
11601                     } else if (className.indexOf('new') !== -1) {
11602                         if (month == 11) {
11603                             month = 0;
11604                             year += 1;
11605                         } else {
11606                             month += 1;
11607                         }
11608                     }
11609                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11610                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11611                     this.fill();
11612                     this.setValue(this.formatDate(this.date));
11613                     this.hide();
11614                 }
11615                 break;
11616         }
11617     },
11618     
11619     setStartDate: function(startDate){
11620         this.startDate = startDate || -Infinity;
11621         if (this.startDate !== -Infinity) {
11622             this.startDate = this.parseDate(this.startDate);
11623         }
11624         this.update();
11625         this.updateNavArrows();
11626     },
11627
11628     setEndDate: function(endDate){
11629         this.endDate = endDate || Infinity;
11630         if (this.endDate !== Infinity) {
11631             this.endDate = this.parseDate(this.endDate);
11632         }
11633         this.update();
11634         this.updateNavArrows();
11635     },
11636     
11637     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11638         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11639         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11640             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11641         }
11642         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11643             return parseInt(d, 10);
11644         });
11645         this.update();
11646         this.updateNavArrows();
11647     },
11648     
11649     updateNavArrows: function() {
11650         var d = new Date(this.viewDate),
11651         year = d.getUTCFullYear(),
11652         month = d.getUTCMonth();
11653         
11654         Roo.each(this.picker().select('.prev', true).elements, function(v){
11655             v.show();
11656             switch (this.viewMode) {
11657                 case 0:
11658
11659                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11660                         v.hide();
11661                     }
11662                     break;
11663                 case 1:
11664                 case 2:
11665                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11666                         v.hide();
11667                     }
11668                     break;
11669             }
11670         });
11671         
11672         Roo.each(this.picker().select('.next', true).elements, function(v){
11673             v.show();
11674             switch (this.viewMode) {
11675                 case 0:
11676
11677                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11678                         v.hide();
11679                     }
11680                     break;
11681                 case 1:
11682                 case 2:
11683                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11684                         v.hide();
11685                     }
11686                     break;
11687             }
11688         })
11689     },
11690     
11691     moveMonth: function(date, dir){
11692         if (!dir) return date;
11693         var new_date = new Date(date.valueOf()),
11694         day = new_date.getUTCDate(),
11695         month = new_date.getUTCMonth(),
11696         mag = Math.abs(dir),
11697         new_month, test;
11698         dir = dir > 0 ? 1 : -1;
11699         if (mag == 1){
11700             test = dir == -1
11701             // If going back one month, make sure month is not current month
11702             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11703             ? function(){
11704                 return new_date.getUTCMonth() == month;
11705             }
11706             // If going forward one month, make sure month is as expected
11707             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11708             : function(){
11709                 return new_date.getUTCMonth() != new_month;
11710             };
11711             new_month = month + dir;
11712             new_date.setUTCMonth(new_month);
11713             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11714             if (new_month < 0 || new_month > 11)
11715                 new_month = (new_month + 12) % 12;
11716         } else {
11717             // For magnitudes >1, move one month at a time...
11718             for (var i=0; i<mag; i++)
11719                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11720                 new_date = this.moveMonth(new_date, dir);
11721             // ...then reset the day, keeping it in the new month
11722             new_month = new_date.getUTCMonth();
11723             new_date.setUTCDate(day);
11724             test = function(){
11725                 return new_month != new_date.getUTCMonth();
11726             };
11727         }
11728         // Common date-resetting loop -- if date is beyond end of month, make it
11729         // end of month
11730         while (test()){
11731             new_date.setUTCDate(--day);
11732             new_date.setUTCMonth(new_month);
11733         }
11734         return new_date;
11735     },
11736
11737     moveYear: function(date, dir){
11738         return this.moveMonth(date, dir*12);
11739     },
11740
11741     dateWithinRange: function(date){
11742         return date >= this.startDate && date <= this.endDate;
11743     },
11744
11745     
11746     remove: function() {
11747         this.picker().remove();
11748     }
11749    
11750 });
11751
11752 Roo.apply(Roo.bootstrap.DateField,  {
11753     
11754     head : {
11755         tag: 'thead',
11756         cn: [
11757         {
11758             tag: 'tr',
11759             cn: [
11760             {
11761                 tag: 'th',
11762                 cls: 'prev',
11763                 html: '<i class="icon-arrow-left"/>'
11764             },
11765             {
11766                 tag: 'th',
11767                 cls: 'switch',
11768                 colspan: '5'
11769             },
11770             {
11771                 tag: 'th',
11772                 cls: 'next',
11773                 html: '<i class="icon-arrow-right"/>'
11774             }
11775
11776             ]
11777         }
11778         ]
11779     },
11780     
11781     content : {
11782         tag: 'tbody',
11783         cn: [
11784         {
11785             tag: 'tr',
11786             cn: [
11787             {
11788                 tag: 'td',
11789                 colspan: '7'
11790             }
11791             ]
11792         }
11793         ]
11794     },
11795     
11796     footer : {
11797         tag: 'tfoot',
11798         cn: [
11799         {
11800             tag: 'tr',
11801             cn: [
11802             {
11803                 tag: 'th',
11804                 colspan: '7',
11805                 cls: 'today'
11806             }
11807                     
11808             ]
11809         }
11810         ]
11811     },
11812     
11813     dates:{
11814         en: {
11815             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11816             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11817             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11818             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11819             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11820             today: "Today"
11821         }
11822     },
11823     
11824     modes: [
11825     {
11826         clsName: 'days',
11827         navFnc: 'Month',
11828         navStep: 1
11829     },
11830     {
11831         clsName: 'months',
11832         navFnc: 'FullYear',
11833         navStep: 1
11834     },
11835     {
11836         clsName: 'years',
11837         navFnc: 'FullYear',
11838         navStep: 10
11839     }]
11840 });
11841
11842 Roo.apply(Roo.bootstrap.DateField,  {
11843   
11844     template : {
11845         tag: 'div',
11846         cls: 'datepicker dropdown-menu',
11847         cn: [
11848         {
11849             tag: 'div',
11850             cls: 'datepicker-days',
11851             cn: [
11852             {
11853                 tag: 'table',
11854                 cls: 'table-condensed',
11855                 cn:[
11856                 Roo.bootstrap.DateField.head,
11857                 {
11858                     tag: 'tbody'
11859                 },
11860                 Roo.bootstrap.DateField.footer
11861                 ]
11862             }
11863             ]
11864         },
11865         {
11866             tag: 'div',
11867             cls: 'datepicker-months',
11868             cn: [
11869             {
11870                 tag: 'table',
11871                 cls: 'table-condensed',
11872                 cn:[
11873                 Roo.bootstrap.DateField.head,
11874                 Roo.bootstrap.DateField.content,
11875                 Roo.bootstrap.DateField.footer
11876                 ]
11877             }
11878             ]
11879         },
11880         {
11881             tag: 'div',
11882             cls: 'datepicker-years',
11883             cn: [
11884             {
11885                 tag: 'table',
11886                 cls: 'table-condensed',
11887                 cn:[
11888                 Roo.bootstrap.DateField.head,
11889                 Roo.bootstrap.DateField.content,
11890                 Roo.bootstrap.DateField.footer
11891                 ]
11892             }
11893             ]
11894         }
11895         ]
11896     }
11897 });
11898
11899  
11900
11901  /*
11902  * - LGPL
11903  *
11904  * TimeField
11905  * 
11906  */
11907
11908 /**
11909  * @class Roo.bootstrap.TimeField
11910  * @extends Roo.bootstrap.Input
11911  * Bootstrap DateField class
11912  * 
11913  * 
11914  * @constructor
11915  * Create a new TimeField
11916  * @param {Object} config The config object
11917  */
11918
11919 Roo.bootstrap.TimeField = function(config){
11920     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11921     this.addEvents({
11922             /**
11923              * @event show
11924              * Fires when this field show.
11925              * @param {Roo.bootstrap.DateField} this
11926              * @param {Mixed} date The date value
11927              */
11928             show : true,
11929             /**
11930              * @event show
11931              * Fires when this field hide.
11932              * @param {Roo.bootstrap.DateField} this
11933              * @param {Mixed} date The date value
11934              */
11935             hide : true,
11936             /**
11937              * @event select
11938              * Fires when select a date.
11939              * @param {Roo.bootstrap.DateField} this
11940              * @param {Mixed} date The date value
11941              */
11942             select : true
11943         });
11944 };
11945
11946 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11947     
11948     /**
11949      * @cfg {String} format
11950      * The default time format string which can be overriden for localization support.  The format must be
11951      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11952      */
11953     format : "H:i",
11954        
11955     onRender: function(ct, position)
11956     {
11957         
11958         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11959                 
11960         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11961         
11962         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11963         
11964         this.pop = this.picker().select('>.datepicker-time',true).first();
11965         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11966         
11967         this.picker().on('mousedown', this.onMousedown, this);
11968         this.picker().on('click', this.onClick, this);
11969         
11970         this.picker().addClass('datepicker-dropdown');
11971     
11972         this.fillTime();
11973         this.update();
11974             
11975         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11976         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11977         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11978         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11979         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11980         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11981
11982     },
11983     
11984     fireKey: function(e){
11985         if (!this.picker().isVisible()){
11986             if (e.keyCode == 27) // allow escape to hide and re-show picker
11987                 this.show();
11988             return;
11989         }
11990
11991         e.preventDefault();
11992         
11993         switch(e.keyCode){
11994             case 27: // escape
11995                 this.hide();
11996                 break;
11997             case 37: // left
11998             case 39: // right
11999                 this.onTogglePeriod();
12000                 break;
12001             case 38: // up
12002                 this.onIncrementMinutes();
12003                 break;
12004             case 40: // down
12005                 this.onDecrementMinutes();
12006                 break;
12007             case 13: // enter
12008             case 9: // tab
12009                 this.setTime();
12010                 break;
12011         }
12012     },
12013     
12014     onClick: function(e) {
12015         e.stopPropagation();
12016         e.preventDefault();
12017     },
12018     
12019     picker : function()
12020     {
12021         return this.el.select('.datepicker', true).first();
12022     },
12023     
12024     fillTime: function()
12025     {    
12026         var time = this.pop.select('tbody', true).first();
12027         
12028         time.dom.innerHTML = '';
12029         
12030         time.createChild({
12031             tag: 'tr',
12032             cn: [
12033                 {
12034                     tag: 'td',
12035                     cn: [
12036                         {
12037                             tag: 'a',
12038                             href: '#',
12039                             cls: 'btn',
12040                             cn: [
12041                                 {
12042                                     tag: 'span',
12043                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12044                                 }
12045                             ]
12046                         } 
12047                     ]
12048                 },
12049                 {
12050                     tag: 'td',
12051                     cls: 'separator'
12052                 },
12053                 {
12054                     tag: 'td',
12055                     cn: [
12056                         {
12057                             tag: 'a',
12058                             href: '#',
12059                             cls: 'btn',
12060                             cn: [
12061                                 {
12062                                     tag: 'span',
12063                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12064                                 }
12065                             ]
12066                         }
12067                     ]
12068                 },
12069                 {
12070                     tag: 'td',
12071                     cls: 'separator'
12072                 }
12073             ]
12074         });
12075         
12076         time.createChild({
12077             tag: 'tr',
12078             cn: [
12079                 {
12080                     tag: 'td',
12081                     cn: [
12082                         {
12083                             tag: 'span',
12084                             cls: 'timepicker-hour',
12085                             html: '00'
12086                         }  
12087                     ]
12088                 },
12089                 {
12090                     tag: 'td',
12091                     cls: 'separator',
12092                     html: ':'
12093                 },
12094                 {
12095                     tag: 'td',
12096                     cn: [
12097                         {
12098                             tag: 'span',
12099                             cls: 'timepicker-minute',
12100                             html: '00'
12101                         }  
12102                     ]
12103                 },
12104                 {
12105                     tag: 'td',
12106                     cls: 'separator'
12107                 },
12108                 {
12109                     tag: 'td',
12110                     cn: [
12111                         {
12112                             tag: 'button',
12113                             type: 'button',
12114                             cls: 'btn btn-primary period',
12115                             html: 'AM'
12116                             
12117                         }
12118                     ]
12119                 }
12120             ]
12121         });
12122         
12123         time.createChild({
12124             tag: 'tr',
12125             cn: [
12126                 {
12127                     tag: 'td',
12128                     cn: [
12129                         {
12130                             tag: 'a',
12131                             href: '#',
12132                             cls: 'btn',
12133                             cn: [
12134                                 {
12135                                     tag: 'span',
12136                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12137                                 }
12138                             ]
12139                         }
12140                     ]
12141                 },
12142                 {
12143                     tag: 'td',
12144                     cls: 'separator'
12145                 },
12146                 {
12147                     tag: 'td',
12148                     cn: [
12149                         {
12150                             tag: 'a',
12151                             href: '#',
12152                             cls: 'btn',
12153                             cn: [
12154                                 {
12155                                     tag: 'span',
12156                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12157                                 }
12158                             ]
12159                         }
12160                     ]
12161                 },
12162                 {
12163                     tag: 'td',
12164                     cls: 'separator'
12165                 }
12166             ]
12167         });
12168         
12169     },
12170     
12171     update: function()
12172     {
12173         
12174         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12175         
12176         this.fill();
12177     },
12178     
12179     fill: function() 
12180     {
12181         var hours = this.time.getHours();
12182         var minutes = this.time.getMinutes();
12183         var period = 'AM';
12184         
12185         if(hours > 11){
12186             period = 'PM';
12187         }
12188         
12189         if(hours == 0){
12190             hours = 12;
12191         }
12192         
12193         
12194         if(hours > 12){
12195             hours = hours - 12;
12196         }
12197         
12198         if(hours < 10){
12199             hours = '0' + hours;
12200         }
12201         
12202         if(minutes < 10){
12203             minutes = '0' + minutes;
12204         }
12205         
12206         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12207         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12208         this.pop.select('button', true).first().dom.innerHTML = period;
12209         
12210     },
12211     
12212     place: function()
12213     {   
12214         this.picker().removeClass(['bottom', 'top']);
12215         
12216         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12217             /*
12218              * place to the top of element!
12219              *
12220              */
12221             
12222             this.picker().addClass('top');
12223             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12224             
12225             return;
12226         }
12227         
12228         this.picker().addClass('bottom');
12229         
12230         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12231     },
12232   
12233     onFocus : function()
12234     {
12235         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12236         this.show();
12237     },
12238     
12239     onBlur : function()
12240     {
12241         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12242         this.hide();
12243     },
12244     
12245     show : function()
12246     {
12247         this.picker().show();
12248         this.pop.show();
12249         this.update();
12250         this.place();
12251         
12252         this.fireEvent('show', this, this.date);
12253     },
12254     
12255     hide : function()
12256     {
12257         this.picker().hide();
12258         this.pop.hide();
12259         
12260         this.fireEvent('hide', this, this.date);
12261     },
12262     
12263     setTime : function()
12264     {
12265         this.hide();
12266         this.setValue(this.time.format(this.format));
12267         
12268         this.fireEvent('select', this, this.date);
12269         
12270         
12271     },
12272     
12273     onMousedown: function(e){
12274         e.stopPropagation();
12275         e.preventDefault();
12276     },
12277     
12278     onIncrementHours: function()
12279     {
12280         Roo.log('onIncrementHours');
12281         this.time = this.time.add(Date.HOUR, 1);
12282         this.update();
12283         
12284     },
12285     
12286     onDecrementHours: function()
12287     {
12288         Roo.log('onDecrementHours');
12289         this.time = this.time.add(Date.HOUR, -1);
12290         this.update();
12291     },
12292     
12293     onIncrementMinutes: function()
12294     {
12295         Roo.log('onIncrementMinutes');
12296         this.time = this.time.add(Date.MINUTE, 1);
12297         this.update();
12298     },
12299     
12300     onDecrementMinutes: function()
12301     {
12302         Roo.log('onDecrementMinutes');
12303         this.time = this.time.add(Date.MINUTE, -1);
12304         this.update();
12305     },
12306     
12307     onTogglePeriod: function()
12308     {
12309         Roo.log('onTogglePeriod');
12310         this.time = this.time.add(Date.HOUR, 12);
12311         this.update();
12312     }
12313     
12314    
12315 });
12316
12317 Roo.apply(Roo.bootstrap.TimeField,  {
12318     
12319     content : {
12320         tag: 'tbody',
12321         cn: [
12322             {
12323                 tag: 'tr',
12324                 cn: [
12325                 {
12326                     tag: 'td',
12327                     colspan: '7'
12328                 }
12329                 ]
12330             }
12331         ]
12332     },
12333     
12334     footer : {
12335         tag: 'tfoot',
12336         cn: [
12337             {
12338                 tag: 'tr',
12339                 cn: [
12340                 {
12341                     tag: 'th',
12342                     colspan: '7',
12343                     cls: '',
12344                     cn: [
12345                         {
12346                             tag: 'button',
12347                             cls: 'btn btn-info ok',
12348                             html: 'OK'
12349                         }
12350                     ]
12351                 }
12352
12353                 ]
12354             }
12355         ]
12356     }
12357 });
12358
12359 Roo.apply(Roo.bootstrap.TimeField,  {
12360   
12361     template : {
12362         tag: 'div',
12363         cls: 'datepicker dropdown-menu',
12364         cn: [
12365             {
12366                 tag: 'div',
12367                 cls: 'datepicker-time',
12368                 cn: [
12369                 {
12370                     tag: 'table',
12371                     cls: 'table-condensed',
12372                     cn:[
12373                     Roo.bootstrap.TimeField.content,
12374                     Roo.bootstrap.TimeField.footer
12375                     ]
12376                 }
12377                 ]
12378             }
12379         ]
12380     }
12381 });
12382
12383  
12384
12385  /*
12386  * - LGPL
12387  *
12388  * CheckBox
12389  * 
12390  */
12391
12392 /**
12393  * @class Roo.bootstrap.CheckBox
12394  * @extends Roo.bootstrap.Input
12395  * Bootstrap CheckBox class
12396  * 
12397  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12398  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12399  * @cfg {String} boxLabel The text that appears beside the checkbox
12400  * @cfg {Boolean} checked initnal the element
12401  * 
12402  * @constructor
12403  * Create a new CheckBox
12404  * @param {Object} config The config object
12405  */
12406
12407 Roo.bootstrap.CheckBox = function(config){
12408     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12409    
12410         this.addEvents({
12411             /**
12412             * @event check
12413             * Fires when the element is checked or unchecked.
12414             * @param {Roo.bootstrap.CheckBox} this This input
12415             * @param {Boolean} checked The new checked value
12416             */
12417            check : true
12418         });
12419 };
12420
12421 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12422     
12423     inputType: 'checkbox',
12424     inputValue: 1,
12425     valueOff: 0,
12426     boxLabel: false,
12427     checked: false,
12428     
12429     getAutoCreate : function()
12430     {
12431         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12432         
12433         var id = Roo.id();
12434         
12435         var cfg = {};
12436         
12437         cfg.cls = 'form-group' //input-group
12438         
12439         var input =  {
12440             tag: 'input',
12441             id : id,
12442             type : this.inputType,
12443             value : (!this.checked) ? this.valueOff : this.inputValue,
12444             cls : 'form-box',
12445             placeholder : this.placeholder || ''
12446             
12447         };
12448         
12449         if (this.disabled) {
12450             input.disabled=true;
12451         }
12452         
12453         if(this.checked){
12454             input.checked = this.checked;
12455         }
12456         
12457         if (this.name) {
12458             input.name = this.name;
12459         }
12460         
12461         if (this.size) {
12462             input.cls += ' input-' + this.size;
12463         }
12464         
12465         var settings=this;
12466         ['xs','sm','md','lg'].map(function(size){
12467             if (settings[size]) {
12468                 cfg.cls += ' col-' + size + '-' + settings[size];
12469             }
12470         });
12471         
12472         var inputblock = input;
12473         
12474         if (this.before || this.after) {
12475             
12476             inputblock = {
12477                 cls : 'input-group',
12478                 cn :  [] 
12479             };
12480             if (this.before) {
12481                 inputblock.cn.push({
12482                     tag :'span',
12483                     cls : 'input-group-addon',
12484                     html : this.before
12485                 });
12486             }
12487             inputblock.cn.push(input);
12488             if (this.after) {
12489                 inputblock.cn.push({
12490                     tag :'span',
12491                     cls : 'input-group-addon',
12492                     html : this.after
12493                 });
12494             }
12495             
12496         };
12497         
12498         if (align ==='left' && this.fieldLabel.length) {
12499                 Roo.log("left and has label");
12500                 cfg.cn = [
12501                     
12502                     {
12503                         tag: 'label',
12504                         'for' :  id,
12505                         cls : 'control-label col-md-' + this.labelWidth,
12506                         html : this.fieldLabel
12507                         
12508                     },
12509                     {
12510                         cls : "col-md-" + (12 - this.labelWidth), 
12511                         cn: [
12512                             inputblock
12513                         ]
12514                     }
12515                     
12516                 ];
12517         } else if ( this.fieldLabel.length) {
12518                 Roo.log(" label");
12519                 cfg.cn = [
12520                    
12521                     {
12522                         tag: this.boxLabel ? 'span' : 'label',
12523                         'for': id,
12524                         cls: 'control-label box-input-label',
12525                         //cls : 'input-group-addon',
12526                         html : this.fieldLabel
12527                         
12528                     },
12529                     
12530                     inputblock
12531                     
12532                 ];
12533
12534         } else {
12535             
12536                    Roo.log(" no label && no align");
12537                 cfg.cn = [
12538                     
12539                         inputblock
12540                     
12541                 ];
12542                 
12543                 
12544         };
12545         
12546         if(this.boxLabel){
12547             cfg.cn.push({
12548                 tag: 'label',
12549                 'for': id,
12550                 cls: 'box-label',
12551                 html: this.boxLabel
12552             })
12553         }
12554         
12555         return cfg;
12556         
12557     },
12558     
12559     /**
12560      * return the real input element.
12561      */
12562     inputEl: function ()
12563     {
12564         return this.el.select('input.form-box',true).first();
12565     },
12566     
12567     initEvents : function()
12568     {
12569 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12570         
12571         this.inputEl().on('click', this.onClick,  this);
12572         
12573     },
12574     
12575     onClick : function()
12576     {   
12577         this.setChecked(!this.checked);
12578     },
12579     
12580     setChecked : function(state,suppressEvent)
12581     {
12582         this.checked = state;
12583         
12584         this.inputEl().dom.checked = state;
12585         
12586         if(suppressEvent !== true){
12587             this.fireEvent('check', this, state);
12588         }
12589         
12590         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12591         
12592     },
12593     
12594     setValue : function(v,suppressEvent)
12595     {
12596         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12597     }
12598     
12599 });
12600
12601  
12602 /*
12603  * - LGPL
12604  *
12605  * Radio
12606  * 
12607  */
12608
12609 /**
12610  * @class Roo.bootstrap.Radio
12611  * @extends Roo.bootstrap.CheckBox
12612  * Bootstrap Radio class
12613
12614  * @constructor
12615  * Create a new Radio
12616  * @param {Object} config The config object
12617  */
12618
12619 Roo.bootstrap.Radio = function(config){
12620     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12621    
12622 };
12623
12624 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12625     
12626     inputType: 'radio',
12627     inputValue: '',
12628     valueOff: '',
12629     
12630     getAutoCreate : function()
12631     {
12632         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12633         
12634         var id = Roo.id();
12635         
12636         var cfg = {};
12637         
12638         cfg.cls = 'form-group' //input-group
12639         
12640         var input =  {
12641             tag: 'input',
12642             id : id,
12643             type : this.inputType,
12644             value : (!this.checked) ? this.valueOff : this.inputValue,
12645             cls : 'form-box',
12646             placeholder : this.placeholder || ''
12647             
12648         };
12649         
12650         if (this.disabled) {
12651             input.disabled=true;
12652         }
12653         
12654         if(this.checked){
12655             input.checked = this.checked;
12656         }
12657         
12658         if (this.name) {
12659             input.name = this.name;
12660         }
12661         
12662         if (this.size) {
12663             input.cls += ' input-' + this.size;
12664         }
12665         
12666         var settings=this;
12667         ['xs','sm','md','lg'].map(function(size){
12668             if (settings[size]) {
12669                 cfg.cls += ' col-' + size + '-' + settings[size];
12670             }
12671         });
12672         
12673         var inputblock = input;
12674         
12675         if (this.before || this.after) {
12676             
12677             inputblock = {
12678                 cls : 'input-group',
12679                 cn :  [] 
12680             };
12681             if (this.before) {
12682                 inputblock.cn.push({
12683                     tag :'span',
12684                     cls : 'input-group-addon',
12685                     html : this.before
12686                 });
12687             }
12688             inputblock.cn.push(input);
12689             if (this.after) {
12690                 inputblock.cn.push({
12691                     tag :'span',
12692                     cls : 'input-group-addon',
12693                     html : this.after
12694                 });
12695             }
12696             
12697         };
12698         
12699         if (align ==='left' && this.fieldLabel.length) {
12700                 Roo.log("left and has label");
12701                 cfg.cn = [
12702                     
12703                     {
12704                         tag: 'label',
12705                         'for' :  id,
12706                         cls : 'control-label col-md-' + this.labelWidth,
12707                         html : this.fieldLabel
12708                         
12709                     },
12710                     {
12711                         cls : "col-md-" + (12 - this.labelWidth), 
12712                         cn: [
12713                             inputblock
12714                         ]
12715                     }
12716                     
12717                 ];
12718         } else if ( this.fieldLabel.length) {
12719                 Roo.log(" label");
12720                  cfg.cn = [
12721                    
12722                     {
12723                         tag: 'label',
12724                         'for': id,
12725                         cls: 'control-label box-input-label',
12726                         //cls : 'input-group-addon',
12727                         html : this.fieldLabel
12728                         
12729                     },
12730                     
12731                     inputblock
12732                     
12733                 ];
12734
12735         } else {
12736             
12737                    Roo.log(" no label && no align");
12738                 cfg.cn = [
12739                     
12740                         inputblock
12741                     
12742                 ];
12743                 
12744                 
12745         };
12746         
12747         if(this.boxLabel){
12748             cfg.cn.push({
12749                 tag: 'label',
12750                 'for': id,
12751                 cls: 'box-label',
12752                 html: this.boxLabel
12753             })
12754         }
12755         
12756         return cfg;
12757         
12758     },
12759    
12760     onClick : function()
12761     {   
12762         this.setChecked(true);
12763     },
12764     
12765     setChecked : function(state,suppressEvent)
12766     {
12767         if(state){
12768             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12769                 v.dom.checked = false;
12770             });
12771         }
12772         
12773         this.checked = state;
12774         this.inputEl().dom.checked = state;
12775         
12776         if(suppressEvent !== true){
12777             this.fireEvent('check', this, state);
12778         }
12779         
12780         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12781         
12782     },
12783     
12784     getGroupValue : function()
12785     {
12786         var value = ''
12787         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12788             if(v.dom.checked == true){
12789                 value = v.dom.value;
12790             }
12791         });
12792         
12793         return value;
12794     },
12795     
12796     /**
12797      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12798      * @return {Mixed} value The field value
12799      */
12800     getValue : function(){
12801         return this.getGroupValue();
12802     }
12803     
12804 });
12805
12806  
12807 //<script type="text/javascript">
12808
12809 /*
12810  * Based  Ext JS Library 1.1.1
12811  * Copyright(c) 2006-2007, Ext JS, LLC.
12812  * LGPL
12813  *
12814  */
12815  
12816 /**
12817  * @class Roo.HtmlEditorCore
12818  * @extends Roo.Component
12819  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12820  *
12821  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12822  */
12823
12824 Roo.HtmlEditorCore = function(config){
12825     
12826     
12827     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12828     this.addEvents({
12829         /**
12830          * @event initialize
12831          * Fires when the editor is fully initialized (including the iframe)
12832          * @param {Roo.HtmlEditorCore} this
12833          */
12834         initialize: true,
12835         /**
12836          * @event activate
12837          * Fires when the editor is first receives the focus. Any insertion must wait
12838          * until after this event.
12839          * @param {Roo.HtmlEditorCore} this
12840          */
12841         activate: true,
12842          /**
12843          * @event beforesync
12844          * Fires before the textarea is updated with content from the editor iframe. Return false
12845          * to cancel the sync.
12846          * @param {Roo.HtmlEditorCore} this
12847          * @param {String} html
12848          */
12849         beforesync: true,
12850          /**
12851          * @event beforepush
12852          * Fires before the iframe editor is updated with content from the textarea. Return false
12853          * to cancel the push.
12854          * @param {Roo.HtmlEditorCore} this
12855          * @param {String} html
12856          */
12857         beforepush: true,
12858          /**
12859          * @event sync
12860          * Fires when the textarea is updated with content from the editor iframe.
12861          * @param {Roo.HtmlEditorCore} this
12862          * @param {String} html
12863          */
12864         sync: true,
12865          /**
12866          * @event push
12867          * Fires when the iframe editor is updated with content from the textarea.
12868          * @param {Roo.HtmlEditorCore} this
12869          * @param {String} html
12870          */
12871         push: true,
12872         
12873         /**
12874          * @event editorevent
12875          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12876          * @param {Roo.HtmlEditorCore} this
12877          */
12878         editorevent: true
12879     });
12880      
12881 };
12882
12883
12884 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12885
12886
12887      /**
12888      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12889      */
12890     
12891     owner : false,
12892     
12893      /**
12894      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12895      *                        Roo.resizable.
12896      */
12897     resizable : false,
12898      /**
12899      * @cfg {Number} height (in pixels)
12900      */   
12901     height: 300,
12902    /**
12903      * @cfg {Number} width (in pixels)
12904      */   
12905     width: 500,
12906     
12907     /**
12908      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12909      * 
12910      */
12911     stylesheets: false,
12912     
12913     // id of frame..
12914     frameId: false,
12915     
12916     // private properties
12917     validationEvent : false,
12918     deferHeight: true,
12919     initialized : false,
12920     activated : false,
12921     sourceEditMode : false,
12922     onFocus : Roo.emptyFn,
12923     iframePad:3,
12924     hideMode:'offsets',
12925     
12926      
12927     
12928
12929     /**
12930      * Protected method that will not generally be called directly. It
12931      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12932      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12933      */
12934     getDocMarkup : function(){
12935         // body styles..
12936         var st = '';
12937         Roo.log(this.stylesheets);
12938         
12939         // inherit styels from page...?? 
12940         if (this.stylesheets === false) {
12941             
12942             Roo.get(document.head).select('style').each(function(node) {
12943                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12944             });
12945             
12946             Roo.get(document.head).select('link').each(function(node) { 
12947                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12948             });
12949             
12950         } else if (!this.stylesheets.length) {
12951                 // simple..
12952                 st = '<style type="text/css">' +
12953                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12954                    '</style>';
12955         } else {
12956             Roo.each(this.stylesheets, function(s) {
12957                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12958             });
12959             
12960         }
12961         
12962         st +=  '<style type="text/css">' +
12963             'IMG { cursor: pointer } ' +
12964         '</style>';
12965
12966         
12967         return '<html><head>' + st  +
12968             //<style type="text/css">' +
12969             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12970             //'</style>' +
12971             ' </head><body class="roo-htmleditor-body"></body></html>';
12972     },
12973
12974     // private
12975     onRender : function(ct, position)
12976     {
12977         var _t = this;
12978         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12979         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12980         
12981         
12982         this.el.dom.style.border = '0 none';
12983         this.el.dom.setAttribute('tabIndex', -1);
12984         this.el.addClass('x-hidden hide');
12985         
12986         
12987         
12988         if(Roo.isIE){ // fix IE 1px bogus margin
12989             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12990         }
12991        
12992         
12993         this.frameId = Roo.id();
12994         
12995          
12996         
12997         var iframe = this.owner.wrap.createChild({
12998             tag: 'iframe',
12999             cls: 'form-control', // bootstrap..
13000             id: this.frameId,
13001             name: this.frameId,
13002             frameBorder : 'no',
13003             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13004         }, this.el
13005         );
13006         
13007         
13008         this.iframe = iframe.dom;
13009
13010          this.assignDocWin();
13011         
13012         this.doc.designMode = 'on';
13013        
13014         this.doc.open();
13015         this.doc.write(this.getDocMarkup());
13016         this.doc.close();
13017
13018         
13019         var task = { // must defer to wait for browser to be ready
13020             run : function(){
13021                 //console.log("run task?" + this.doc.readyState);
13022                 this.assignDocWin();
13023                 if(this.doc.body || this.doc.readyState == 'complete'){
13024                     try {
13025                         this.doc.designMode="on";
13026                     } catch (e) {
13027                         return;
13028                     }
13029                     Roo.TaskMgr.stop(task);
13030                     this.initEditor.defer(10, this);
13031                 }
13032             },
13033             interval : 10,
13034             duration: 10000,
13035             scope: this
13036         };
13037         Roo.TaskMgr.start(task);
13038
13039         
13040          
13041     },
13042
13043     // private
13044     onResize : function(w, h)
13045     {
13046          Roo.log('resize: ' +w + ',' + h );
13047         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13048         if(!this.iframe){
13049             return;
13050         }
13051         if(typeof w == 'number'){
13052             
13053             this.iframe.style.width = w + 'px';
13054         }
13055         if(typeof h == 'number'){
13056             
13057             this.iframe.style.height = h + 'px';
13058             if(this.doc){
13059                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13060             }
13061         }
13062         
13063     },
13064
13065     /**
13066      * Toggles the editor between standard and source edit mode.
13067      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13068      */
13069     toggleSourceEdit : function(sourceEditMode){
13070         
13071         this.sourceEditMode = sourceEditMode === true;
13072         
13073         if(this.sourceEditMode){
13074  
13075             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13076             
13077         }else{
13078             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13079             //this.iframe.className = '';
13080             this.deferFocus();
13081         }
13082         //this.setSize(this.owner.wrap.getSize());
13083         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13084     },
13085
13086     
13087   
13088
13089     /**
13090      * Protected method that will not generally be called directly. If you need/want
13091      * custom HTML cleanup, this is the method you should override.
13092      * @param {String} html The HTML to be cleaned
13093      * return {String} The cleaned HTML
13094      */
13095     cleanHtml : function(html){
13096         html = String(html);
13097         if(html.length > 5){
13098             if(Roo.isSafari){ // strip safari nonsense
13099                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13100             }
13101         }
13102         if(html == '&nbsp;'){
13103             html = '';
13104         }
13105         return html;
13106     },
13107
13108     /**
13109      * HTML Editor -> Textarea
13110      * Protected method that will not generally be called directly. Syncs the contents
13111      * of the editor iframe with the textarea.
13112      */
13113     syncValue : function(){
13114         if(this.initialized){
13115             var bd = (this.doc.body || this.doc.documentElement);
13116             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13117             var html = bd.innerHTML;
13118             if(Roo.isSafari){
13119                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13120                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13121                 if(m && m[1]){
13122                     html = '<div style="'+m[0]+'">' + html + '</div>';
13123                 }
13124             }
13125             html = this.cleanHtml(html);
13126             // fix up the special chars.. normaly like back quotes in word...
13127             // however we do not want to do this with chinese..
13128             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13129                 var cc = b.charCodeAt();
13130                 if (
13131                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13132                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13133                     (cc >= 0xf900 && cc < 0xfb00 )
13134                 ) {
13135                         return b;
13136                 }
13137                 return "&#"+cc+";" 
13138             });
13139             if(this.owner.fireEvent('beforesync', this, html) !== false){
13140                 this.el.dom.value = html;
13141                 this.owner.fireEvent('sync', this, html);
13142             }
13143         }
13144     },
13145
13146     /**
13147      * Protected method that will not generally be called directly. Pushes the value of the textarea
13148      * into the iframe editor.
13149      */
13150     pushValue : function(){
13151         if(this.initialized){
13152             var v = this.el.dom.value.trim();
13153             
13154 //            if(v.length < 1){
13155 //                v = '&#160;';
13156 //            }
13157             
13158             if(this.owner.fireEvent('beforepush', this, v) !== false){
13159                 var d = (this.doc.body || this.doc.documentElement);
13160                 d.innerHTML = v;
13161                 this.cleanUpPaste();
13162                 this.el.dom.value = d.innerHTML;
13163                 this.owner.fireEvent('push', this, v);
13164             }
13165         }
13166     },
13167
13168     // private
13169     deferFocus : function(){
13170         this.focus.defer(10, this);
13171     },
13172
13173     // doc'ed in Field
13174     focus : function(){
13175         if(this.win && !this.sourceEditMode){
13176             this.win.focus();
13177         }else{
13178             this.el.focus();
13179         }
13180     },
13181     
13182     assignDocWin: function()
13183     {
13184         var iframe = this.iframe;
13185         
13186          if(Roo.isIE){
13187             this.doc = iframe.contentWindow.document;
13188             this.win = iframe.contentWindow;
13189         } else {
13190             if (!Roo.get(this.frameId)) {
13191                 return;
13192             }
13193             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13194             this.win = Roo.get(this.frameId).dom.contentWindow;
13195         }
13196     },
13197     
13198     // private
13199     initEditor : function(){
13200         //console.log("INIT EDITOR");
13201         this.assignDocWin();
13202         
13203         
13204         
13205         this.doc.designMode="on";
13206         this.doc.open();
13207         this.doc.write(this.getDocMarkup());
13208         this.doc.close();
13209         
13210         var dbody = (this.doc.body || this.doc.documentElement);
13211         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13212         // this copies styles from the containing element into thsi one..
13213         // not sure why we need all of this..
13214         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13215         ss['background-attachment'] = 'fixed'; // w3c
13216         dbody.bgProperties = 'fixed'; // ie
13217         Roo.DomHelper.applyStyles(dbody, ss);
13218         Roo.EventManager.on(this.doc, {
13219             //'mousedown': this.onEditorEvent,
13220             'mouseup': this.onEditorEvent,
13221             'dblclick': this.onEditorEvent,
13222             'click': this.onEditorEvent,
13223             'keyup': this.onEditorEvent,
13224             buffer:100,
13225             scope: this
13226         });
13227         if(Roo.isGecko){
13228             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13229         }
13230         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13231             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13232         }
13233         this.initialized = true;
13234
13235         this.owner.fireEvent('initialize', this);
13236         this.pushValue();
13237     },
13238
13239     // private
13240     onDestroy : function(){
13241         
13242         
13243         
13244         if(this.rendered){
13245             
13246             //for (var i =0; i < this.toolbars.length;i++) {
13247             //    // fixme - ask toolbars for heights?
13248             //    this.toolbars[i].onDestroy();
13249            // }
13250             
13251             //this.wrap.dom.innerHTML = '';
13252             //this.wrap.remove();
13253         }
13254     },
13255
13256     // private
13257     onFirstFocus : function(){
13258         
13259         this.assignDocWin();
13260         
13261         
13262         this.activated = true;
13263          
13264     
13265         if(Roo.isGecko){ // prevent silly gecko errors
13266             this.win.focus();
13267             var s = this.win.getSelection();
13268             if(!s.focusNode || s.focusNode.nodeType != 3){
13269                 var r = s.getRangeAt(0);
13270                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13271                 r.collapse(true);
13272                 this.deferFocus();
13273             }
13274             try{
13275                 this.execCmd('useCSS', true);
13276                 this.execCmd('styleWithCSS', false);
13277             }catch(e){}
13278         }
13279         this.owner.fireEvent('activate', this);
13280     },
13281
13282     // private
13283     adjustFont: function(btn){
13284         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13285         //if(Roo.isSafari){ // safari
13286         //    adjust *= 2;
13287        // }
13288         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13289         if(Roo.isSafari){ // safari
13290             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13291             v =  (v < 10) ? 10 : v;
13292             v =  (v > 48) ? 48 : v;
13293             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13294             
13295         }
13296         
13297         
13298         v = Math.max(1, v+adjust);
13299         
13300         this.execCmd('FontSize', v  );
13301     },
13302
13303     onEditorEvent : function(e){
13304         this.owner.fireEvent('editorevent', this, e);
13305       //  this.updateToolbar();
13306         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13307     },
13308
13309     insertTag : function(tg)
13310     {
13311         // could be a bit smarter... -> wrap the current selected tRoo..
13312         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13313             
13314             range = this.createRange(this.getSelection());
13315             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13316             wrappingNode.appendChild(range.extractContents());
13317             range.insertNode(wrappingNode);
13318
13319             return;
13320             
13321             
13322             
13323         }
13324         this.execCmd("formatblock",   tg);
13325         
13326     },
13327     
13328     insertText : function(txt)
13329     {
13330         
13331         
13332         var range = this.createRange();
13333         range.deleteContents();
13334                //alert(Sender.getAttribute('label'));
13335                
13336         range.insertNode(this.doc.createTextNode(txt));
13337     } ,
13338     
13339      
13340
13341     /**
13342      * Executes a Midas editor command on the editor document and performs necessary focus and
13343      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13344      * @param {String} cmd The Midas command
13345      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13346      */
13347     relayCmd : function(cmd, value){
13348         this.win.focus();
13349         this.execCmd(cmd, value);
13350         this.owner.fireEvent('editorevent', this);
13351         //this.updateToolbar();
13352         this.owner.deferFocus();
13353     },
13354
13355     /**
13356      * Executes a Midas editor command directly on the editor document.
13357      * For visual commands, you should use {@link #relayCmd} instead.
13358      * <b>This should only be called after the editor is initialized.</b>
13359      * @param {String} cmd The Midas command
13360      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13361      */
13362     execCmd : function(cmd, value){
13363         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13364         this.syncValue();
13365     },
13366  
13367  
13368    
13369     /**
13370      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13371      * to insert tRoo.
13372      * @param {String} text | dom node.. 
13373      */
13374     insertAtCursor : function(text)
13375     {
13376         
13377         
13378         
13379         if(!this.activated){
13380             return;
13381         }
13382         /*
13383         if(Roo.isIE){
13384             this.win.focus();
13385             var r = this.doc.selection.createRange();
13386             if(r){
13387                 r.collapse(true);
13388                 r.pasteHTML(text);
13389                 this.syncValue();
13390                 this.deferFocus();
13391             
13392             }
13393             return;
13394         }
13395         */
13396         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13397             this.win.focus();
13398             
13399             
13400             // from jquery ui (MIT licenced)
13401             var range, node;
13402             var win = this.win;
13403             
13404             if (win.getSelection && win.getSelection().getRangeAt) {
13405                 range = win.getSelection().getRangeAt(0);
13406                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13407                 range.insertNode(node);
13408             } else if (win.document.selection && win.document.selection.createRange) {
13409                 // no firefox support
13410                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13411                 win.document.selection.createRange().pasteHTML(txt);
13412             } else {
13413                 // no firefox support
13414                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13415                 this.execCmd('InsertHTML', txt);
13416             } 
13417             
13418             this.syncValue();
13419             
13420             this.deferFocus();
13421         }
13422     },
13423  // private
13424     mozKeyPress : function(e){
13425         if(e.ctrlKey){
13426             var c = e.getCharCode(), cmd;
13427           
13428             if(c > 0){
13429                 c = String.fromCharCode(c).toLowerCase();
13430                 switch(c){
13431                     case 'b':
13432                         cmd = 'bold';
13433                         break;
13434                     case 'i':
13435                         cmd = 'italic';
13436                         break;
13437                     
13438                     case 'u':
13439                         cmd = 'underline';
13440                         break;
13441                     
13442                     case 'v':
13443                         this.cleanUpPaste.defer(100, this);
13444                         return;
13445                         
13446                 }
13447                 if(cmd){
13448                     this.win.focus();
13449                     this.execCmd(cmd);
13450                     this.deferFocus();
13451                     e.preventDefault();
13452                 }
13453                 
13454             }
13455         }
13456     },
13457
13458     // private
13459     fixKeys : function(){ // load time branching for fastest keydown performance
13460         if(Roo.isIE){
13461             return function(e){
13462                 var k = e.getKey(), r;
13463                 if(k == e.TAB){
13464                     e.stopEvent();
13465                     r = this.doc.selection.createRange();
13466                     if(r){
13467                         r.collapse(true);
13468                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13469                         this.deferFocus();
13470                     }
13471                     return;
13472                 }
13473                 
13474                 if(k == e.ENTER){
13475                     r = this.doc.selection.createRange();
13476                     if(r){
13477                         var target = r.parentElement();
13478                         if(!target || target.tagName.toLowerCase() != 'li'){
13479                             e.stopEvent();
13480                             r.pasteHTML('<br />');
13481                             r.collapse(false);
13482                             r.select();
13483                         }
13484                     }
13485                 }
13486                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13487                     this.cleanUpPaste.defer(100, this);
13488                     return;
13489                 }
13490                 
13491                 
13492             };
13493         }else if(Roo.isOpera){
13494             return function(e){
13495                 var k = e.getKey();
13496                 if(k == e.TAB){
13497                     e.stopEvent();
13498                     this.win.focus();
13499                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13500                     this.deferFocus();
13501                 }
13502                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13503                     this.cleanUpPaste.defer(100, this);
13504                     return;
13505                 }
13506                 
13507             };
13508         }else if(Roo.isSafari){
13509             return function(e){
13510                 var k = e.getKey();
13511                 
13512                 if(k == e.TAB){
13513                     e.stopEvent();
13514                     this.execCmd('InsertText','\t');
13515                     this.deferFocus();
13516                     return;
13517                 }
13518                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13519                     this.cleanUpPaste.defer(100, this);
13520                     return;
13521                 }
13522                 
13523              };
13524         }
13525     }(),
13526     
13527     getAllAncestors: function()
13528     {
13529         var p = this.getSelectedNode();
13530         var a = [];
13531         if (!p) {
13532             a.push(p); // push blank onto stack..
13533             p = this.getParentElement();
13534         }
13535         
13536         
13537         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13538             a.push(p);
13539             p = p.parentNode;
13540         }
13541         a.push(this.doc.body);
13542         return a;
13543     },
13544     lastSel : false,
13545     lastSelNode : false,
13546     
13547     
13548     getSelection : function() 
13549     {
13550         this.assignDocWin();
13551         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13552     },
13553     
13554     getSelectedNode: function() 
13555     {
13556         // this may only work on Gecko!!!
13557         
13558         // should we cache this!!!!
13559         
13560         
13561         
13562          
13563         var range = this.createRange(this.getSelection()).cloneRange();
13564         
13565         if (Roo.isIE) {
13566             var parent = range.parentElement();
13567             while (true) {
13568                 var testRange = range.duplicate();
13569                 testRange.moveToElementText(parent);
13570                 if (testRange.inRange(range)) {
13571                     break;
13572                 }
13573                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13574                     break;
13575                 }
13576                 parent = parent.parentElement;
13577             }
13578             return parent;
13579         }
13580         
13581         // is ancestor a text element.
13582         var ac =  range.commonAncestorContainer;
13583         if (ac.nodeType == 3) {
13584             ac = ac.parentNode;
13585         }
13586         
13587         var ar = ac.childNodes;
13588          
13589         var nodes = [];
13590         var other_nodes = [];
13591         var has_other_nodes = false;
13592         for (var i=0;i<ar.length;i++) {
13593             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13594                 continue;
13595             }
13596             // fullly contained node.
13597             
13598             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13599                 nodes.push(ar[i]);
13600                 continue;
13601             }
13602             
13603             // probably selected..
13604             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13605                 other_nodes.push(ar[i]);
13606                 continue;
13607             }
13608             // outer..
13609             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13610                 continue;
13611             }
13612             
13613             
13614             has_other_nodes = true;
13615         }
13616         if (!nodes.length && other_nodes.length) {
13617             nodes= other_nodes;
13618         }
13619         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13620             return false;
13621         }
13622         
13623         return nodes[0];
13624     },
13625     createRange: function(sel)
13626     {
13627         // this has strange effects when using with 
13628         // top toolbar - not sure if it's a great idea.
13629         //this.editor.contentWindow.focus();
13630         if (typeof sel != "undefined") {
13631             try {
13632                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13633             } catch(e) {
13634                 return this.doc.createRange();
13635             }
13636         } else {
13637             return this.doc.createRange();
13638         }
13639     },
13640     getParentElement: function()
13641     {
13642         
13643         this.assignDocWin();
13644         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13645         
13646         var range = this.createRange(sel);
13647          
13648         try {
13649             var p = range.commonAncestorContainer;
13650             while (p.nodeType == 3) { // text node
13651                 p = p.parentNode;
13652             }
13653             return p;
13654         } catch (e) {
13655             return null;
13656         }
13657     
13658     },
13659     /***
13660      *
13661      * Range intersection.. the hard stuff...
13662      *  '-1' = before
13663      *  '0' = hits..
13664      *  '1' = after.
13665      *         [ -- selected range --- ]
13666      *   [fail]                        [fail]
13667      *
13668      *    basically..
13669      *      if end is before start or  hits it. fail.
13670      *      if start is after end or hits it fail.
13671      *
13672      *   if either hits (but other is outside. - then it's not 
13673      *   
13674      *    
13675      **/
13676     
13677     
13678     // @see http://www.thismuchiknow.co.uk/?p=64.
13679     rangeIntersectsNode : function(range, node)
13680     {
13681         var nodeRange = node.ownerDocument.createRange();
13682         try {
13683             nodeRange.selectNode(node);
13684         } catch (e) {
13685             nodeRange.selectNodeContents(node);
13686         }
13687     
13688         var rangeStartRange = range.cloneRange();
13689         rangeStartRange.collapse(true);
13690     
13691         var rangeEndRange = range.cloneRange();
13692         rangeEndRange.collapse(false);
13693     
13694         var nodeStartRange = nodeRange.cloneRange();
13695         nodeStartRange.collapse(true);
13696     
13697         var nodeEndRange = nodeRange.cloneRange();
13698         nodeEndRange.collapse(false);
13699     
13700         return rangeStartRange.compareBoundaryPoints(
13701                  Range.START_TO_START, nodeEndRange) == -1 &&
13702                rangeEndRange.compareBoundaryPoints(
13703                  Range.START_TO_START, nodeStartRange) == 1;
13704         
13705          
13706     },
13707     rangeCompareNode : function(range, node)
13708     {
13709         var nodeRange = node.ownerDocument.createRange();
13710         try {
13711             nodeRange.selectNode(node);
13712         } catch (e) {
13713             nodeRange.selectNodeContents(node);
13714         }
13715         
13716         
13717         range.collapse(true);
13718     
13719         nodeRange.collapse(true);
13720      
13721         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13722         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13723          
13724         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13725         
13726         var nodeIsBefore   =  ss == 1;
13727         var nodeIsAfter    = ee == -1;
13728         
13729         if (nodeIsBefore && nodeIsAfter)
13730             return 0; // outer
13731         if (!nodeIsBefore && nodeIsAfter)
13732             return 1; //right trailed.
13733         
13734         if (nodeIsBefore && !nodeIsAfter)
13735             return 2;  // left trailed.
13736         // fully contined.
13737         return 3;
13738     },
13739
13740     // private? - in a new class?
13741     cleanUpPaste :  function()
13742     {
13743         // cleans up the whole document..
13744          Roo.log('cleanuppaste');
13745         this.cleanUpChildren(this.doc.body);
13746         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13747         if (clean != this.doc.body.innerHTML) {
13748             this.doc.body.innerHTML = clean;
13749         }
13750         
13751     },
13752     
13753     cleanWordChars : function(input) {// change the chars to hex code
13754         var he = Roo.HtmlEditorCore;
13755         
13756         var output = input;
13757         Roo.each(he.swapCodes, function(sw) { 
13758             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13759             
13760             output = output.replace(swapper, sw[1]);
13761         });
13762         
13763         return output;
13764     },
13765     
13766     
13767     cleanUpChildren : function (n)
13768     {
13769         if (!n.childNodes.length) {
13770             return;
13771         }
13772         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13773            this.cleanUpChild(n.childNodes[i]);
13774         }
13775     },
13776     
13777     
13778         
13779     
13780     cleanUpChild : function (node)
13781     {
13782         var ed = this;
13783         //console.log(node);
13784         if (node.nodeName == "#text") {
13785             // clean up silly Windows -- stuff?
13786             return; 
13787         }
13788         if (node.nodeName == "#comment") {
13789             node.parentNode.removeChild(node);
13790             // clean up silly Windows -- stuff?
13791             return; 
13792         }
13793         
13794         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
13795             // remove node.
13796             node.parentNode.removeChild(node);
13797             return;
13798             
13799         }
13800         
13801         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13802         
13803         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13804         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13805         
13806         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13807         //    remove_keep_children = true;
13808         //}
13809         
13810         if (remove_keep_children) {
13811             this.cleanUpChildren(node);
13812             // inserts everything just before this node...
13813             while (node.childNodes.length) {
13814                 var cn = node.childNodes[0];
13815                 node.removeChild(cn);
13816                 node.parentNode.insertBefore(cn, node);
13817             }
13818             node.parentNode.removeChild(node);
13819             return;
13820         }
13821         
13822         if (!node.attributes || !node.attributes.length) {
13823             this.cleanUpChildren(node);
13824             return;
13825         }
13826         
13827         function cleanAttr(n,v)
13828         {
13829             
13830             if (v.match(/^\./) || v.match(/^\//)) {
13831                 return;
13832             }
13833             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13834                 return;
13835             }
13836             if (v.match(/^#/)) {
13837                 return;
13838             }
13839 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13840             node.removeAttribute(n);
13841             
13842         }
13843         
13844         function cleanStyle(n,v)
13845         {
13846             if (v.match(/expression/)) { //XSS?? should we even bother..
13847                 node.removeAttribute(n);
13848                 return;
13849             }
13850             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13851             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13852             
13853             
13854             var parts = v.split(/;/);
13855             var clean = [];
13856             
13857             Roo.each(parts, function(p) {
13858                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13859                 if (!p.length) {
13860                     return true;
13861                 }
13862                 var l = p.split(':').shift().replace(/\s+/g,'');
13863                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13864                 
13865                 
13866                 if ( cblack.indexOf(l) > -1) {
13867 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13868                     //node.removeAttribute(n);
13869                     return true;
13870                 }
13871                 //Roo.log()
13872                 // only allow 'c whitelisted system attributes'
13873                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13874 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13875                     //node.removeAttribute(n);
13876                     return true;
13877                 }
13878                 
13879                 
13880                  
13881                 
13882                 clean.push(p);
13883                 return true;
13884             });
13885             if (clean.length) { 
13886                 node.setAttribute(n, clean.join(';'));
13887             } else {
13888                 node.removeAttribute(n);
13889             }
13890             
13891         }
13892         
13893         
13894         for (var i = node.attributes.length-1; i > -1 ; i--) {
13895             var a = node.attributes[i];
13896             //console.log(a);
13897             
13898             if (a.name.toLowerCase().substr(0,2)=='on')  {
13899                 node.removeAttribute(a.name);
13900                 continue;
13901             }
13902             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13903                 node.removeAttribute(a.name);
13904                 continue;
13905             }
13906             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13907                 cleanAttr(a.name,a.value); // fixme..
13908                 continue;
13909             }
13910             if (a.name == 'style') {
13911                 cleanStyle(a.name,a.value);
13912                 continue;
13913             }
13914             /// clean up MS crap..
13915             // tecnically this should be a list of valid class'es..
13916             
13917             
13918             if (a.name == 'class') {
13919                 if (a.value.match(/^Mso/)) {
13920                     node.className = '';
13921                 }
13922                 
13923                 if (a.value.match(/body/)) {
13924                     node.className = '';
13925                 }
13926                 continue;
13927             }
13928             
13929             // style cleanup!?
13930             // class cleanup?
13931             
13932         }
13933         
13934         
13935         this.cleanUpChildren(node);
13936         
13937         
13938     }
13939     
13940     
13941     // hide stuff that is not compatible
13942     /**
13943      * @event blur
13944      * @hide
13945      */
13946     /**
13947      * @event change
13948      * @hide
13949      */
13950     /**
13951      * @event focus
13952      * @hide
13953      */
13954     /**
13955      * @event specialkey
13956      * @hide
13957      */
13958     /**
13959      * @cfg {String} fieldClass @hide
13960      */
13961     /**
13962      * @cfg {String} focusClass @hide
13963      */
13964     /**
13965      * @cfg {String} autoCreate @hide
13966      */
13967     /**
13968      * @cfg {String} inputType @hide
13969      */
13970     /**
13971      * @cfg {String} invalidClass @hide
13972      */
13973     /**
13974      * @cfg {String} invalidText @hide
13975      */
13976     /**
13977      * @cfg {String} msgFx @hide
13978      */
13979     /**
13980      * @cfg {String} validateOnBlur @hide
13981      */
13982 });
13983
13984 Roo.HtmlEditorCore.white = [
13985         'area', 'br', 'img', 'input', 'hr', 'wbr',
13986         
13987        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
13988        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
13989        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
13990        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
13991        'table',   'ul',         'xmp', 
13992        
13993        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
13994       'thead',   'tr', 
13995      
13996       'dir', 'menu', 'ol', 'ul', 'dl',
13997        
13998       'embed',  'object'
13999 ];
14000
14001
14002 Roo.HtmlEditorCore.black = [
14003     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14004         'applet', // 
14005         'base',   'basefont', 'bgsound', 'blink',  'body', 
14006         'frame',  'frameset', 'head',    'html',   'ilayer', 
14007         'iframe', 'layer',  'link',     'meta',    'object',   
14008         'script', 'style' ,'title',  'xml' // clean later..
14009 ];
14010 Roo.HtmlEditorCore.clean = [
14011     'script', 'style', 'title', 'xml'
14012 ];
14013 Roo.HtmlEditorCore.remove = [
14014     'font'
14015 ];
14016 // attributes..
14017
14018 Roo.HtmlEditorCore.ablack = [
14019     'on'
14020 ];
14021     
14022 Roo.HtmlEditorCore.aclean = [ 
14023     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14024 ];
14025
14026 // protocols..
14027 Roo.HtmlEditorCore.pwhite= [
14028         'http',  'https',  'mailto'
14029 ];
14030
14031 // white listed style attributes.
14032 Roo.HtmlEditorCore.cwhite= [
14033       //  'text-align', /// default is to allow most things..
14034       
14035          
14036 //        'font-size'//??
14037 ];
14038
14039 // black listed style attributes.
14040 Roo.HtmlEditorCore.cblack= [
14041       //  'font-size' -- this can be set by the project 
14042 ];
14043
14044
14045 Roo.HtmlEditorCore.swapCodes   =[ 
14046     [    8211, "--" ], 
14047     [    8212, "--" ], 
14048     [    8216,  "'" ],  
14049     [    8217, "'" ],  
14050     [    8220, '"' ],  
14051     [    8221, '"' ],  
14052     [    8226, "*" ],  
14053     [    8230, "..." ]
14054 ]; 
14055
14056     /*
14057  * - LGPL
14058  *
14059  * HtmlEditor
14060  * 
14061  */
14062
14063 /**
14064  * @class Roo.bootstrap.HtmlEditor
14065  * @extends Roo.bootstrap.TextArea
14066  * Bootstrap HtmlEditor class
14067
14068  * @constructor
14069  * Create a new HtmlEditor
14070  * @param {Object} config The config object
14071  */
14072
14073 Roo.bootstrap.HtmlEditor = function(config){
14074     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14075     if (!this.toolbars) {
14076         this.toolbars = [];
14077     }
14078     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14079     this.addEvents({
14080             /**
14081              * @event initialize
14082              * Fires when the editor is fully initialized (including the iframe)
14083              * @param {HtmlEditor} this
14084              */
14085             initialize: true,
14086             /**
14087              * @event activate
14088              * Fires when the editor is first receives the focus. Any insertion must wait
14089              * until after this event.
14090              * @param {HtmlEditor} this
14091              */
14092             activate: true,
14093              /**
14094              * @event beforesync
14095              * Fires before the textarea is updated with content from the editor iframe. Return false
14096              * to cancel the sync.
14097              * @param {HtmlEditor} this
14098              * @param {String} html
14099              */
14100             beforesync: true,
14101              /**
14102              * @event beforepush
14103              * Fires before the iframe editor is updated with content from the textarea. Return false
14104              * to cancel the push.
14105              * @param {HtmlEditor} this
14106              * @param {String} html
14107              */
14108             beforepush: true,
14109              /**
14110              * @event sync
14111              * Fires when the textarea is updated with content from the editor iframe.
14112              * @param {HtmlEditor} this
14113              * @param {String} html
14114              */
14115             sync: true,
14116              /**
14117              * @event push
14118              * Fires when the iframe editor is updated with content from the textarea.
14119              * @param {HtmlEditor} this
14120              * @param {String} html
14121              */
14122             push: true,
14123              /**
14124              * @event editmodechange
14125              * Fires when the editor switches edit modes
14126              * @param {HtmlEditor} this
14127              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14128              */
14129             editmodechange: true,
14130             /**
14131              * @event editorevent
14132              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14133              * @param {HtmlEditor} this
14134              */
14135             editorevent: true,
14136             /**
14137              * @event firstfocus
14138              * Fires when on first focus - needed by toolbars..
14139              * @param {HtmlEditor} this
14140              */
14141             firstfocus: true,
14142             /**
14143              * @event autosave
14144              * Auto save the htmlEditor value as a file into Events
14145              * @param {HtmlEditor} this
14146              */
14147             autosave: true,
14148             /**
14149              * @event savedpreview
14150              * preview the saved version of htmlEditor
14151              * @param {HtmlEditor} this
14152              */
14153             savedpreview: true
14154         });
14155 };
14156
14157
14158 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14159     
14160     
14161       /**
14162      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14163      */
14164     toolbars : false,
14165    
14166      /**
14167      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14168      *                        Roo.resizable.
14169      */
14170     resizable : false,
14171      /**
14172      * @cfg {Number} height (in pixels)
14173      */   
14174     height: 300,
14175    /**
14176      * @cfg {Number} width (in pixels)
14177      */   
14178     width: false,
14179     
14180     /**
14181      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14182      * 
14183      */
14184     stylesheets: false,
14185     
14186     // id of frame..
14187     frameId: false,
14188     
14189     // private properties
14190     validationEvent : false,
14191     deferHeight: true,
14192     initialized : false,
14193     activated : false,
14194     
14195     onFocus : Roo.emptyFn,
14196     iframePad:3,
14197     hideMode:'offsets',
14198     
14199     
14200     tbContainer : false,
14201     
14202     toolbarContainer :function() {
14203         return this.wrap.select('.x-html-editor-tb',true).first();
14204     },
14205
14206     /**
14207      * Protected method that will not generally be called directly. It
14208      * is called when the editor creates its toolbar. Override this method if you need to
14209      * add custom toolbar buttons.
14210      * @param {HtmlEditor} editor
14211      */
14212     createToolbar : function(){
14213         
14214         Roo.log("create toolbars");
14215         
14216         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14217         this.toolbars[0].render(this.toolbarContainer());
14218         
14219         return;
14220         
14221 //        if (!editor.toolbars || !editor.toolbars.length) {
14222 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14223 //        }
14224 //        
14225 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14226 //            editor.toolbars[i] = Roo.factory(
14227 //                    typeof(editor.toolbars[i]) == 'string' ?
14228 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14229 //                Roo.bootstrap.HtmlEditor);
14230 //            editor.toolbars[i].init(editor);
14231 //        }
14232     },
14233
14234      
14235     // private
14236     onRender : function(ct, position)
14237     {
14238        // Roo.log("Call onRender: " + this.xtype);
14239         var _t = this;
14240         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14241       
14242         this.wrap = this.inputEl().wrap({
14243             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14244         });
14245         
14246         this.editorcore.onRender(ct, position);
14247          
14248         if (this.resizable) {
14249             this.resizeEl = new Roo.Resizable(this.wrap, {
14250                 pinned : true,
14251                 wrap: true,
14252                 dynamic : true,
14253                 minHeight : this.height,
14254                 height: this.height,
14255                 handles : this.resizable,
14256                 width: this.width,
14257                 listeners : {
14258                     resize : function(r, w, h) {
14259                         _t.onResize(w,h); // -something
14260                     }
14261                 }
14262             });
14263             
14264         }
14265         this.createToolbar(this);
14266        
14267         
14268         if(!this.width && this.resizable){
14269             this.setSize(this.wrap.getSize());
14270         }
14271         if (this.resizeEl) {
14272             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14273             // should trigger onReize..
14274         }
14275         
14276     },
14277
14278     // private
14279     onResize : function(w, h)
14280     {
14281         Roo.log('resize: ' +w + ',' + h );
14282         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14283         var ew = false;
14284         var eh = false;
14285         
14286         if(this.inputEl() ){
14287             if(typeof w == 'number'){
14288                 var aw = w - this.wrap.getFrameWidth('lr');
14289                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14290                 ew = aw;
14291             }
14292             if(typeof h == 'number'){
14293                  var tbh = -11;  // fixme it needs to tool bar size!
14294                 for (var i =0; i < this.toolbars.length;i++) {
14295                     // fixme - ask toolbars for heights?
14296                     tbh += this.toolbars[i].el.getHeight();
14297                     //if (this.toolbars[i].footer) {
14298                     //    tbh += this.toolbars[i].footer.el.getHeight();
14299                     //}
14300                 }
14301               
14302                 
14303                 
14304                 
14305                 
14306                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14307                 ah -= 5; // knock a few pixes off for look..
14308                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14309                 var eh = ah;
14310             }
14311         }
14312         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14313         this.editorcore.onResize(ew,eh);
14314         
14315     },
14316
14317     /**
14318      * Toggles the editor between standard and source edit mode.
14319      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14320      */
14321     toggleSourceEdit : function(sourceEditMode)
14322     {
14323         this.editorcore.toggleSourceEdit(sourceEditMode);
14324         
14325         if(this.editorcore.sourceEditMode){
14326             Roo.log('editor - showing textarea');
14327             
14328 //            Roo.log('in');
14329 //            Roo.log(this.syncValue());
14330             this.syncValue();
14331             this.inputEl().removeClass('hide');
14332             this.inputEl().dom.removeAttribute('tabIndex');
14333             this.inputEl().focus();
14334         }else{
14335             Roo.log('editor - hiding textarea');
14336 //            Roo.log('out')
14337 //            Roo.log(this.pushValue()); 
14338             this.pushValue();
14339             
14340             this.inputEl().addClass('hide');
14341             this.inputEl().dom.setAttribute('tabIndex', -1);
14342             //this.deferFocus();
14343         }
14344          
14345         if(this.resizable){
14346             this.setSize(this.wrap.getSize());
14347         }
14348         
14349         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14350     },
14351  
14352     // private (for BoxComponent)
14353     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14354
14355     // private (for BoxComponent)
14356     getResizeEl : function(){
14357         return this.wrap;
14358     },
14359
14360     // private (for BoxComponent)
14361     getPositionEl : function(){
14362         return this.wrap;
14363     },
14364
14365     // private
14366     initEvents : function(){
14367         this.originalValue = this.getValue();
14368     },
14369
14370 //    /**
14371 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14372 //     * @method
14373 //     */
14374 //    markInvalid : Roo.emptyFn,
14375 //    /**
14376 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14377 //     * @method
14378 //     */
14379 //    clearInvalid : Roo.emptyFn,
14380
14381     setValue : function(v){
14382         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14383         this.editorcore.pushValue();
14384     },
14385
14386      
14387     // private
14388     deferFocus : function(){
14389         this.focus.defer(10, this);
14390     },
14391
14392     // doc'ed in Field
14393     focus : function(){
14394         this.editorcore.focus();
14395         
14396     },
14397       
14398
14399     // private
14400     onDestroy : function(){
14401         
14402         
14403         
14404         if(this.rendered){
14405             
14406             for (var i =0; i < this.toolbars.length;i++) {
14407                 // fixme - ask toolbars for heights?
14408                 this.toolbars[i].onDestroy();
14409             }
14410             
14411             this.wrap.dom.innerHTML = '';
14412             this.wrap.remove();
14413         }
14414     },
14415
14416     // private
14417     onFirstFocus : function(){
14418         //Roo.log("onFirstFocus");
14419         this.editorcore.onFirstFocus();
14420          for (var i =0; i < this.toolbars.length;i++) {
14421             this.toolbars[i].onFirstFocus();
14422         }
14423         
14424     },
14425     
14426     // private
14427     syncValue : function()
14428     {   
14429         this.editorcore.syncValue();
14430     },
14431     
14432     pushValue : function()
14433     {   
14434         this.editorcore.pushValue();
14435     }
14436      
14437     
14438     // hide stuff that is not compatible
14439     /**
14440      * @event blur
14441      * @hide
14442      */
14443     /**
14444      * @event change
14445      * @hide
14446      */
14447     /**
14448      * @event focus
14449      * @hide
14450      */
14451     /**
14452      * @event specialkey
14453      * @hide
14454      */
14455     /**
14456      * @cfg {String} fieldClass @hide
14457      */
14458     /**
14459      * @cfg {String} focusClass @hide
14460      */
14461     /**
14462      * @cfg {String} autoCreate @hide
14463      */
14464     /**
14465      * @cfg {String} inputType @hide
14466      */
14467     /**
14468      * @cfg {String} invalidClass @hide
14469      */
14470     /**
14471      * @cfg {String} invalidText @hide
14472      */
14473     /**
14474      * @cfg {String} msgFx @hide
14475      */
14476     /**
14477      * @cfg {String} validateOnBlur @hide
14478      */
14479 });
14480  
14481     
14482    
14483    
14484    
14485       
14486
14487 /**
14488  * @class Roo.bootstrap.HtmlEditorToolbar1
14489  * Basic Toolbar
14490  * 
14491  * Usage:
14492  *
14493  new Roo.bootstrap.HtmlEditor({
14494     ....
14495     toolbars : [
14496         new Roo.bootstrap.HtmlEditorToolbar1({
14497             disable : { fonts: 1 , format: 1, ..., ... , ...],
14498             btns : [ .... ]
14499         })
14500     }
14501      
14502  * 
14503  * @cfg {Object} disable List of elements to disable..
14504  * @cfg {Array} btns List of additional buttons.
14505  * 
14506  * 
14507  * NEEDS Extra CSS? 
14508  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14509  */
14510  
14511 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14512 {
14513     
14514     Roo.apply(this, config);
14515     
14516     // default disabled, based on 'good practice'..
14517     this.disable = this.disable || {};
14518     Roo.applyIf(this.disable, {
14519         fontSize : true,
14520         colors : true,
14521         specialElements : true
14522     });
14523     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14524     
14525     this.editor = config.editor;
14526     this.editorcore = config.editor.editorcore;
14527     
14528     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14529     
14530     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14531     // dont call parent... till later.
14532 }
14533 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14534     
14535     
14536     bar : true,
14537     
14538     editor : false,
14539     editorcore : false,
14540     
14541     
14542     formats : [
14543         "p" ,  
14544         "h1","h2","h3","h4","h5","h6", 
14545         "pre", "code", 
14546         "abbr", "acronym", "address", "cite", "samp", "var",
14547         'div','span'
14548     ],
14549     
14550     onRender : function(ct, position)
14551     {
14552        // Roo.log("Call onRender: " + this.xtype);
14553         
14554        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14555        Roo.log(this.el);
14556        this.el.dom.style.marginBottom = '0';
14557        var _this = this;
14558        var editorcore = this.editorcore;
14559        var editor= this.editor;
14560        
14561        var children = [];
14562        var btn = function(id,cmd , toggle, handler){
14563        
14564             var  event = toggle ? 'toggle' : 'click';
14565        
14566             var a = {
14567                 size : 'sm',
14568                 xtype: 'Button',
14569                 xns: Roo.bootstrap,
14570                 glyphicon : id,
14571                 cmd : id || cmd,
14572                 enableToggle:toggle !== false,
14573                 //html : 'submit'
14574                 pressed : toggle ? false : null,
14575                 listeners : {}
14576             }
14577             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14578                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14579             }
14580             children.push(a);
14581             return a;
14582        }
14583         
14584         var style = {
14585                 xtype: 'Button',
14586                 size : 'sm',
14587                 xns: Roo.bootstrap,
14588                 glyphicon : 'font',
14589                 //html : 'submit'
14590                 menu : {
14591                     xtype: 'Menu',
14592                     xns: Roo.bootstrap,
14593                     items:  []
14594                 }
14595         };
14596         Roo.each(this.formats, function(f) {
14597             style.menu.items.push({
14598                 xtype :'MenuItem',
14599                 xns: Roo.bootstrap,
14600                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14601                 tagname : f,
14602                 listeners : {
14603                     click : function()
14604                     {
14605                         editorcore.insertTag(this.tagname);
14606                         editor.focus();
14607                     }
14608                 }
14609                 
14610             });
14611         });
14612          children.push(style);   
14613             
14614             
14615         btn('bold',false,true);
14616         btn('italic',false,true);
14617         btn('align-left', 'justifyleft',true);
14618         btn('align-center', 'justifycenter',true);
14619         btn('align-right' , 'justifyright',true);
14620         btn('link', false, false, function(btn) {
14621             //Roo.log("create link?");
14622             var url = prompt(this.createLinkText, this.defaultLinkValue);
14623             if(url && url != 'http:/'+'/'){
14624                 this.editorcore.relayCmd('createlink', url);
14625             }
14626         }),
14627         btn('list','insertunorderedlist',true);
14628         btn('pencil', false,true, function(btn){
14629                 Roo.log(this);
14630                 
14631                 this.toggleSourceEdit(btn.pressed);
14632         });
14633         /*
14634         var cog = {
14635                 xtype: 'Button',
14636                 size : 'sm',
14637                 xns: Roo.bootstrap,
14638                 glyphicon : 'cog',
14639                 //html : 'submit'
14640                 menu : {
14641                     xtype: 'Menu',
14642                     xns: Roo.bootstrap,
14643                     items:  []
14644                 }
14645         };
14646         
14647         cog.menu.items.push({
14648             xtype :'MenuItem',
14649             xns: Roo.bootstrap,
14650             html : Clean styles,
14651             tagname : f,
14652             listeners : {
14653                 click : function()
14654                 {
14655                     editorcore.insertTag(this.tagname);
14656                     editor.focus();
14657                 }
14658             }
14659             
14660         });
14661        */
14662         
14663          
14664        this.xtype = 'Navbar';
14665         
14666         for(var i=0;i< children.length;i++) {
14667             
14668             this.buttons.add(this.addxtypeChild(children[i]));
14669             
14670         }
14671         
14672         editor.on('editorevent', this.updateToolbar, this);
14673     },
14674     onBtnClick : function(id)
14675     {
14676        this.editorcore.relayCmd(id);
14677        this.editorcore.focus();
14678     },
14679     
14680     /**
14681      * Protected method that will not generally be called directly. It triggers
14682      * a toolbar update by reading the markup state of the current selection in the editor.
14683      */
14684     updateToolbar: function(){
14685
14686         if(!this.editorcore.activated){
14687             this.editor.onFirstFocus(); // is this neeed?
14688             return;
14689         }
14690
14691         var btns = this.buttons; 
14692         var doc = this.editorcore.doc;
14693         btns.get('bold').setActive(doc.queryCommandState('bold'));
14694         btns.get('italic').setActive(doc.queryCommandState('italic'));
14695         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14696         
14697         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14698         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14699         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14700         
14701         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14702         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14703          /*
14704         
14705         var ans = this.editorcore.getAllAncestors();
14706         if (this.formatCombo) {
14707             
14708             
14709             var store = this.formatCombo.store;
14710             this.formatCombo.setValue("");
14711             for (var i =0; i < ans.length;i++) {
14712                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14713                     // select it..
14714                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14715                     break;
14716                 }
14717             }
14718         }
14719         
14720         
14721         
14722         // hides menus... - so this cant be on a menu...
14723         Roo.bootstrap.MenuMgr.hideAll();
14724         */
14725         Roo.bootstrap.MenuMgr.hideAll();
14726         //this.editorsyncValue();
14727     },
14728     onFirstFocus: function() {
14729         this.buttons.each(function(item){
14730            item.enable();
14731         });
14732     },
14733     toggleSourceEdit : function(sourceEditMode){
14734         
14735           
14736         if(sourceEditMode){
14737             Roo.log("disabling buttons");
14738            this.buttons.each( function(item){
14739                 if(item.cmd != 'pencil'){
14740                     item.disable();
14741                 }
14742             });
14743           
14744         }else{
14745             Roo.log("enabling buttons");
14746             if(this.editorcore.initialized){
14747                 this.buttons.each( function(item){
14748                     item.enable();
14749                 });
14750             }
14751             
14752         }
14753         Roo.log("calling toggole on editor");
14754         // tell the editor that it's been pressed..
14755         this.editor.toggleSourceEdit(sourceEditMode);
14756        
14757     }
14758 });
14759
14760
14761
14762
14763
14764 /**
14765  * @class Roo.bootstrap.Table.AbstractSelectionModel
14766  * @extends Roo.util.Observable
14767  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14768  * implemented by descendant classes.  This class should not be directly instantiated.
14769  * @constructor
14770  */
14771 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14772     this.locked = false;
14773     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14774 };
14775
14776
14777 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14778     /** @ignore Called by the grid automatically. Do not call directly. */
14779     init : function(grid){
14780         this.grid = grid;
14781         this.initEvents();
14782     },
14783
14784     /**
14785      * Locks the selections.
14786      */
14787     lock : function(){
14788         this.locked = true;
14789     },
14790
14791     /**
14792      * Unlocks the selections.
14793      */
14794     unlock : function(){
14795         this.locked = false;
14796     },
14797
14798     /**
14799      * Returns true if the selections are locked.
14800      * @return {Boolean}
14801      */
14802     isLocked : function(){
14803         return this.locked;
14804     }
14805 });
14806 /**
14807  * @class Roo.bootstrap.Table.ColumnModel
14808  * @extends Roo.util.Observable
14809  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14810  * the columns in the table.
14811  
14812  * @constructor
14813  * @param {Object} config An Array of column config objects. See this class's
14814  * config objects for details.
14815 */
14816 Roo.bootstrap.Table.ColumnModel = function(config){
14817         /**
14818      * The config passed into the constructor
14819      */
14820     this.config = config;
14821     this.lookup = {};
14822
14823     // if no id, create one
14824     // if the column does not have a dataIndex mapping,
14825     // map it to the order it is in the config
14826     for(var i = 0, len = config.length; i < len; i++){
14827         var c = config[i];
14828         if(typeof c.dataIndex == "undefined"){
14829             c.dataIndex = i;
14830         }
14831         if(typeof c.renderer == "string"){
14832             c.renderer = Roo.util.Format[c.renderer];
14833         }
14834         if(typeof c.id == "undefined"){
14835             c.id = Roo.id();
14836         }
14837 //        if(c.editor && c.editor.xtype){
14838 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14839 //        }
14840 //        if(c.editor && c.editor.isFormField){
14841 //            c.editor = new Roo.grid.GridEditor(c.editor);
14842 //        }
14843
14844         this.lookup[c.id] = c;
14845     }
14846
14847     /**
14848      * The width of columns which have no width specified (defaults to 100)
14849      * @type Number
14850      */
14851     this.defaultWidth = 100;
14852
14853     /**
14854      * Default sortable of columns which have no sortable specified (defaults to false)
14855      * @type Boolean
14856      */
14857     this.defaultSortable = false;
14858
14859     this.addEvents({
14860         /**
14861              * @event widthchange
14862              * Fires when the width of a column changes.
14863              * @param {ColumnModel} this
14864              * @param {Number} columnIndex The column index
14865              * @param {Number} newWidth The new width
14866              */
14867             "widthchange": true,
14868         /**
14869              * @event headerchange
14870              * Fires when the text of a header changes.
14871              * @param {ColumnModel} this
14872              * @param {Number} columnIndex The column index
14873              * @param {Number} newText The new header text
14874              */
14875             "headerchange": true,
14876         /**
14877              * @event hiddenchange
14878              * Fires when a column is hidden or "unhidden".
14879              * @param {ColumnModel} this
14880              * @param {Number} columnIndex The column index
14881              * @param {Boolean} hidden true if hidden, false otherwise
14882              */
14883             "hiddenchange": true,
14884             /**
14885          * @event columnmoved
14886          * Fires when a column is moved.
14887          * @param {ColumnModel} this
14888          * @param {Number} oldIndex
14889          * @param {Number} newIndex
14890          */
14891         "columnmoved" : true,
14892         /**
14893          * @event columlockchange
14894          * Fires when a column's locked state is changed
14895          * @param {ColumnModel} this
14896          * @param {Number} colIndex
14897          * @param {Boolean} locked true if locked
14898          */
14899         "columnlockchange" : true
14900     });
14901     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14902 };
14903 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14904     /**
14905      * @cfg {String} header The header text to display in the Grid view.
14906      */
14907     /**
14908      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14909      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14910      * specified, the column's index is used as an index into the Record's data Array.
14911      */
14912     /**
14913      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14914      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14915      */
14916     /**
14917      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14918      * Defaults to the value of the {@link #defaultSortable} property.
14919      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14920      */
14921     /**
14922      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14923      */
14924     /**
14925      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14926      */
14927     /**
14928      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14929      */
14930     /**
14931      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14932      */
14933     /**
14934      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14935      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14936      * default renderer uses the raw data value.
14937      */
14938     /**
14939      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14940      */
14941
14942     /**
14943      * Returns the id of the column at the specified index.
14944      * @param {Number} index The column index
14945      * @return {String} the id
14946      */
14947     getColumnId : function(index){
14948         return this.config[index].id;
14949     },
14950
14951     /**
14952      * Returns the column for a specified id.
14953      * @param {String} id The column id
14954      * @return {Object} the column
14955      */
14956     getColumnById : function(id){
14957         return this.lookup[id];
14958     },
14959
14960     
14961     /**
14962      * Returns the column for a specified dataIndex.
14963      * @param {String} dataIndex The column dataIndex
14964      * @return {Object|Boolean} the column or false if not found
14965      */
14966     getColumnByDataIndex: function(dataIndex){
14967         var index = this.findColumnIndex(dataIndex);
14968         return index > -1 ? this.config[index] : false;
14969     },
14970     
14971     /**
14972      * Returns the index for a specified column id.
14973      * @param {String} id The column id
14974      * @return {Number} the index, or -1 if not found
14975      */
14976     getIndexById : function(id){
14977         for(var i = 0, len = this.config.length; i < len; i++){
14978             if(this.config[i].id == id){
14979                 return i;
14980             }
14981         }
14982         return -1;
14983     },
14984     
14985     /**
14986      * Returns the index for a specified column dataIndex.
14987      * @param {String} dataIndex The column dataIndex
14988      * @return {Number} the index, or -1 if not found
14989      */
14990     
14991     findColumnIndex : function(dataIndex){
14992         for(var i = 0, len = this.config.length; i < len; i++){
14993             if(this.config[i].dataIndex == dataIndex){
14994                 return i;
14995             }
14996         }
14997         return -1;
14998     },
14999     
15000     
15001     moveColumn : function(oldIndex, newIndex){
15002         var c = this.config[oldIndex];
15003         this.config.splice(oldIndex, 1);
15004         this.config.splice(newIndex, 0, c);
15005         this.dataMap = null;
15006         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15007     },
15008
15009     isLocked : function(colIndex){
15010         return this.config[colIndex].locked === true;
15011     },
15012
15013     setLocked : function(colIndex, value, suppressEvent){
15014         if(this.isLocked(colIndex) == value){
15015             return;
15016         }
15017         this.config[colIndex].locked = value;
15018         if(!suppressEvent){
15019             this.fireEvent("columnlockchange", this, colIndex, value);
15020         }
15021     },
15022
15023     getTotalLockedWidth : function(){
15024         var totalWidth = 0;
15025         for(var i = 0; i < this.config.length; i++){
15026             if(this.isLocked(i) && !this.isHidden(i)){
15027                 this.totalWidth += this.getColumnWidth(i);
15028             }
15029         }
15030         return totalWidth;
15031     },
15032
15033     getLockedCount : function(){
15034         for(var i = 0, len = this.config.length; i < len; i++){
15035             if(!this.isLocked(i)){
15036                 return i;
15037             }
15038         }
15039     },
15040
15041     /**
15042      * Returns the number of columns.
15043      * @return {Number}
15044      */
15045     getColumnCount : function(visibleOnly){
15046         if(visibleOnly === true){
15047             var c = 0;
15048             for(var i = 0, len = this.config.length; i < len; i++){
15049                 if(!this.isHidden(i)){
15050                     c++;
15051                 }
15052             }
15053             return c;
15054         }
15055         return this.config.length;
15056     },
15057
15058     /**
15059      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15060      * @param {Function} fn
15061      * @param {Object} scope (optional)
15062      * @return {Array} result
15063      */
15064     getColumnsBy : function(fn, scope){
15065         var r = [];
15066         for(var i = 0, len = this.config.length; i < len; i++){
15067             var c = this.config[i];
15068             if(fn.call(scope||this, c, i) === true){
15069                 r[r.length] = c;
15070             }
15071         }
15072         return r;
15073     },
15074
15075     /**
15076      * Returns true if the specified column is sortable.
15077      * @param {Number} col The column index
15078      * @return {Boolean}
15079      */
15080     isSortable : function(col){
15081         if(typeof this.config[col].sortable == "undefined"){
15082             return this.defaultSortable;
15083         }
15084         return this.config[col].sortable;
15085     },
15086
15087     /**
15088      * Returns the rendering (formatting) function defined for the column.
15089      * @param {Number} col The column index.
15090      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15091      */
15092     getRenderer : function(col){
15093         if(!this.config[col].renderer){
15094             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15095         }
15096         return this.config[col].renderer;
15097     },
15098
15099     /**
15100      * Sets the rendering (formatting) function for a column.
15101      * @param {Number} col The column index
15102      * @param {Function} fn The function to use to process the cell's raw data
15103      * to return HTML markup for the grid view. The render function is called with
15104      * the following parameters:<ul>
15105      * <li>Data value.</li>
15106      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15107      * <li>css A CSS style string to apply to the table cell.</li>
15108      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15109      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15110      * <li>Row index</li>
15111      * <li>Column index</li>
15112      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15113      */
15114     setRenderer : function(col, fn){
15115         this.config[col].renderer = fn;
15116     },
15117
15118     /**
15119      * Returns the width for the specified column.
15120      * @param {Number} col The column index
15121      * @return {Number}
15122      */
15123     getColumnWidth : function(col){
15124         return this.config[col].width * 1 || this.defaultWidth;
15125     },
15126
15127     /**
15128      * Sets the width for a column.
15129      * @param {Number} col The column index
15130      * @param {Number} width The new width
15131      */
15132     setColumnWidth : function(col, width, suppressEvent){
15133         this.config[col].width = width;
15134         this.totalWidth = null;
15135         if(!suppressEvent){
15136              this.fireEvent("widthchange", this, col, width);
15137         }
15138     },
15139
15140     /**
15141      * Returns the total width of all columns.
15142      * @param {Boolean} includeHidden True to include hidden column widths
15143      * @return {Number}
15144      */
15145     getTotalWidth : function(includeHidden){
15146         if(!this.totalWidth){
15147             this.totalWidth = 0;
15148             for(var i = 0, len = this.config.length; i < len; i++){
15149                 if(includeHidden || !this.isHidden(i)){
15150                     this.totalWidth += this.getColumnWidth(i);
15151                 }
15152             }
15153         }
15154         return this.totalWidth;
15155     },
15156
15157     /**
15158      * Returns the header for the specified column.
15159      * @param {Number} col The column index
15160      * @return {String}
15161      */
15162     getColumnHeader : function(col){
15163         return this.config[col].header;
15164     },
15165
15166     /**
15167      * Sets the header for a column.
15168      * @param {Number} col The column index
15169      * @param {String} header The new header
15170      */
15171     setColumnHeader : function(col, header){
15172         this.config[col].header = header;
15173         this.fireEvent("headerchange", this, col, header);
15174     },
15175
15176     /**
15177      * Returns the tooltip for the specified column.
15178      * @param {Number} col The column index
15179      * @return {String}
15180      */
15181     getColumnTooltip : function(col){
15182             return this.config[col].tooltip;
15183     },
15184     /**
15185      * Sets the tooltip for a column.
15186      * @param {Number} col The column index
15187      * @param {String} tooltip The new tooltip
15188      */
15189     setColumnTooltip : function(col, tooltip){
15190             this.config[col].tooltip = tooltip;
15191     },
15192
15193     /**
15194      * Returns the dataIndex for the specified column.
15195      * @param {Number} col The column index
15196      * @return {Number}
15197      */
15198     getDataIndex : function(col){
15199         return this.config[col].dataIndex;
15200     },
15201
15202     /**
15203      * Sets the dataIndex for a column.
15204      * @param {Number} col The column index
15205      * @param {Number} dataIndex The new dataIndex
15206      */
15207     setDataIndex : function(col, dataIndex){
15208         this.config[col].dataIndex = dataIndex;
15209     },
15210
15211     
15212     
15213     /**
15214      * Returns true if the cell is editable.
15215      * @param {Number} colIndex The column index
15216      * @param {Number} rowIndex The row index
15217      * @return {Boolean}
15218      */
15219     isCellEditable : function(colIndex, rowIndex){
15220         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15221     },
15222
15223     /**
15224      * Returns the editor defined for the cell/column.
15225      * return false or null to disable editing.
15226      * @param {Number} colIndex The column index
15227      * @param {Number} rowIndex The row index
15228      * @return {Object}
15229      */
15230     getCellEditor : function(colIndex, rowIndex){
15231         return this.config[colIndex].editor;
15232     },
15233
15234     /**
15235      * Sets if a column is editable.
15236      * @param {Number} col The column index
15237      * @param {Boolean} editable True if the column is editable
15238      */
15239     setEditable : function(col, editable){
15240         this.config[col].editable = editable;
15241     },
15242
15243
15244     /**
15245      * Returns true if the column is hidden.
15246      * @param {Number} colIndex The column index
15247      * @return {Boolean}
15248      */
15249     isHidden : function(colIndex){
15250         return this.config[colIndex].hidden;
15251     },
15252
15253
15254     /**
15255      * Returns true if the column width cannot be changed
15256      */
15257     isFixed : function(colIndex){
15258         return this.config[colIndex].fixed;
15259     },
15260
15261     /**
15262      * Returns true if the column can be resized
15263      * @return {Boolean}
15264      */
15265     isResizable : function(colIndex){
15266         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15267     },
15268     /**
15269      * Sets if a column is hidden.
15270      * @param {Number} colIndex The column index
15271      * @param {Boolean} hidden True if the column is hidden
15272      */
15273     setHidden : function(colIndex, hidden){
15274         this.config[colIndex].hidden = hidden;
15275         this.totalWidth = null;
15276         this.fireEvent("hiddenchange", this, colIndex, hidden);
15277     },
15278
15279     /**
15280      * Sets the editor for a column.
15281      * @param {Number} col The column index
15282      * @param {Object} editor The editor object
15283      */
15284     setEditor : function(col, editor){
15285         this.config[col].editor = editor;
15286     }
15287 });
15288
15289 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15290         if(typeof value == "string" && value.length < 1){
15291             return "&#160;";
15292         }
15293         return value;
15294 };
15295
15296 // Alias for backwards compatibility
15297 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15298
15299 /**
15300  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15301  * @class Roo.bootstrap.Table.RowSelectionModel
15302  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15303  * It supports multiple selections and keyboard selection/navigation. 
15304  * @constructor
15305  * @param {Object} config
15306  */
15307
15308 Roo.bootstrap.Table.RowSelectionModel = function(config){
15309     Roo.apply(this, config);
15310     this.selections = new Roo.util.MixedCollection(false, function(o){
15311         return o.id;
15312     });
15313
15314     this.last = false;
15315     this.lastActive = false;
15316
15317     this.addEvents({
15318         /**
15319              * @event selectionchange
15320              * Fires when the selection changes
15321              * @param {SelectionModel} this
15322              */
15323             "selectionchange" : true,
15324         /**
15325              * @event afterselectionchange
15326              * Fires after the selection changes (eg. by key press or clicking)
15327              * @param {SelectionModel} this
15328              */
15329             "afterselectionchange" : true,
15330         /**
15331              * @event beforerowselect
15332              * Fires when a row is selected being selected, return false to cancel.
15333              * @param {SelectionModel} this
15334              * @param {Number} rowIndex The selected index
15335              * @param {Boolean} keepExisting False if other selections will be cleared
15336              */
15337             "beforerowselect" : true,
15338         /**
15339              * @event rowselect
15340              * Fires when a row is selected.
15341              * @param {SelectionModel} this
15342              * @param {Number} rowIndex The selected index
15343              * @param {Roo.data.Record} r The record
15344              */
15345             "rowselect" : true,
15346         /**
15347              * @event rowdeselect
15348              * Fires when a row is deselected.
15349              * @param {SelectionModel} this
15350              * @param {Number} rowIndex The selected index
15351              */
15352         "rowdeselect" : true
15353     });
15354     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15355     this.locked = false;
15356 };
15357
15358 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15359     /**
15360      * @cfg {Boolean} singleSelect
15361      * True to allow selection of only one row at a time (defaults to false)
15362      */
15363     singleSelect : false,
15364
15365     // private
15366     initEvents : function(){
15367
15368         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15369             this.grid.on("mousedown", this.handleMouseDown, this);
15370         }else{ // allow click to work like normal
15371             this.grid.on("rowclick", this.handleDragableRowClick, this);
15372         }
15373
15374         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15375             "up" : function(e){
15376                 if(!e.shiftKey){
15377                     this.selectPrevious(e.shiftKey);
15378                 }else if(this.last !== false && this.lastActive !== false){
15379                     var last = this.last;
15380                     this.selectRange(this.last,  this.lastActive-1);
15381                     this.grid.getView().focusRow(this.lastActive);
15382                     if(last !== false){
15383                         this.last = last;
15384                     }
15385                 }else{
15386                     this.selectFirstRow();
15387                 }
15388                 this.fireEvent("afterselectionchange", this);
15389             },
15390             "down" : function(e){
15391                 if(!e.shiftKey){
15392                     this.selectNext(e.shiftKey);
15393                 }else if(this.last !== false && this.lastActive !== false){
15394                     var last = this.last;
15395                     this.selectRange(this.last,  this.lastActive+1);
15396                     this.grid.getView().focusRow(this.lastActive);
15397                     if(last !== false){
15398                         this.last = last;
15399                     }
15400                 }else{
15401                     this.selectFirstRow();
15402                 }
15403                 this.fireEvent("afterselectionchange", this);
15404             },
15405             scope: this
15406         });
15407
15408         var view = this.grid.view;
15409         view.on("refresh", this.onRefresh, this);
15410         view.on("rowupdated", this.onRowUpdated, this);
15411         view.on("rowremoved", this.onRemove, this);
15412     },
15413
15414     // private
15415     onRefresh : function(){
15416         var ds = this.grid.dataSource, i, v = this.grid.view;
15417         var s = this.selections;
15418         s.each(function(r){
15419             if((i = ds.indexOfId(r.id)) != -1){
15420                 v.onRowSelect(i);
15421             }else{
15422                 s.remove(r);
15423             }
15424         });
15425     },
15426
15427     // private
15428     onRemove : function(v, index, r){
15429         this.selections.remove(r);
15430     },
15431
15432     // private
15433     onRowUpdated : function(v, index, r){
15434         if(this.isSelected(r)){
15435             v.onRowSelect(index);
15436         }
15437     },
15438
15439     /**
15440      * Select records.
15441      * @param {Array} records The records to select
15442      * @param {Boolean} keepExisting (optional) True to keep existing selections
15443      */
15444     selectRecords : function(records, keepExisting){
15445         if(!keepExisting){
15446             this.clearSelections();
15447         }
15448         var ds = this.grid.dataSource;
15449         for(var i = 0, len = records.length; i < len; i++){
15450             this.selectRow(ds.indexOf(records[i]), true);
15451         }
15452     },
15453
15454     /**
15455      * Gets the number of selected rows.
15456      * @return {Number}
15457      */
15458     getCount : function(){
15459         return this.selections.length;
15460     },
15461
15462     /**
15463      * Selects the first row in the grid.
15464      */
15465     selectFirstRow : function(){
15466         this.selectRow(0);
15467     },
15468
15469     /**
15470      * Select the last row.
15471      * @param {Boolean} keepExisting (optional) True to keep existing selections
15472      */
15473     selectLastRow : function(keepExisting){
15474         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15475     },
15476
15477     /**
15478      * Selects the row immediately following the last selected row.
15479      * @param {Boolean} keepExisting (optional) True to keep existing selections
15480      */
15481     selectNext : function(keepExisting){
15482         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15483             this.selectRow(this.last+1, keepExisting);
15484             this.grid.getView().focusRow(this.last);
15485         }
15486     },
15487
15488     /**
15489      * Selects the row that precedes the last selected row.
15490      * @param {Boolean} keepExisting (optional) True to keep existing selections
15491      */
15492     selectPrevious : function(keepExisting){
15493         if(this.last){
15494             this.selectRow(this.last-1, keepExisting);
15495             this.grid.getView().focusRow(this.last);
15496         }
15497     },
15498
15499     /**
15500      * Returns the selected records
15501      * @return {Array} Array of selected records
15502      */
15503     getSelections : function(){
15504         return [].concat(this.selections.items);
15505     },
15506
15507     /**
15508      * Returns the first selected record.
15509      * @return {Record}
15510      */
15511     getSelected : function(){
15512         return this.selections.itemAt(0);
15513     },
15514
15515
15516     /**
15517      * Clears all selections.
15518      */
15519     clearSelections : function(fast){
15520         if(this.locked) return;
15521         if(fast !== true){
15522             var ds = this.grid.dataSource;
15523             var s = this.selections;
15524             s.each(function(r){
15525                 this.deselectRow(ds.indexOfId(r.id));
15526             }, this);
15527             s.clear();
15528         }else{
15529             this.selections.clear();
15530         }
15531         this.last = false;
15532     },
15533
15534
15535     /**
15536      * Selects all rows.
15537      */
15538     selectAll : function(){
15539         if(this.locked) return;
15540         this.selections.clear();
15541         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15542             this.selectRow(i, true);
15543         }
15544     },
15545
15546     /**
15547      * Returns True if there is a selection.
15548      * @return {Boolean}
15549      */
15550     hasSelection : function(){
15551         return this.selections.length > 0;
15552     },
15553
15554     /**
15555      * Returns True if the specified row is selected.
15556      * @param {Number/Record} record The record or index of the record to check
15557      * @return {Boolean}
15558      */
15559     isSelected : function(index){
15560         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15561         return (r && this.selections.key(r.id) ? true : false);
15562     },
15563
15564     /**
15565      * Returns True if the specified record id is selected.
15566      * @param {String} id The id of record to check
15567      * @return {Boolean}
15568      */
15569     isIdSelected : function(id){
15570         return (this.selections.key(id) ? true : false);
15571     },
15572
15573     // private
15574     handleMouseDown : function(e, t){
15575         var view = this.grid.getView(), rowIndex;
15576         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15577             return;
15578         };
15579         if(e.shiftKey && this.last !== false){
15580             var last = this.last;
15581             this.selectRange(last, rowIndex, e.ctrlKey);
15582             this.last = last; // reset the last
15583             view.focusRow(rowIndex);
15584         }else{
15585             var isSelected = this.isSelected(rowIndex);
15586             if(e.button !== 0 && isSelected){
15587                 view.focusRow(rowIndex);
15588             }else if(e.ctrlKey && isSelected){
15589                 this.deselectRow(rowIndex);
15590             }else if(!isSelected){
15591                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15592                 view.focusRow(rowIndex);
15593             }
15594         }
15595         this.fireEvent("afterselectionchange", this);
15596     },
15597     // private
15598     handleDragableRowClick :  function(grid, rowIndex, e) 
15599     {
15600         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15601             this.selectRow(rowIndex, false);
15602             grid.view.focusRow(rowIndex);
15603              this.fireEvent("afterselectionchange", this);
15604         }
15605     },
15606     
15607     /**
15608      * Selects multiple rows.
15609      * @param {Array} rows Array of the indexes of the row to select
15610      * @param {Boolean} keepExisting (optional) True to keep existing selections
15611      */
15612     selectRows : function(rows, keepExisting){
15613         if(!keepExisting){
15614             this.clearSelections();
15615         }
15616         for(var i = 0, len = rows.length; i < len; i++){
15617             this.selectRow(rows[i], true);
15618         }
15619     },
15620
15621     /**
15622      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15623      * @param {Number} startRow The index of the first row in the range
15624      * @param {Number} endRow The index of the last row in the range
15625      * @param {Boolean} keepExisting (optional) True to retain existing selections
15626      */
15627     selectRange : function(startRow, endRow, keepExisting){
15628         if(this.locked) return;
15629         if(!keepExisting){
15630             this.clearSelections();
15631         }
15632         if(startRow <= endRow){
15633             for(var i = startRow; i <= endRow; i++){
15634                 this.selectRow(i, true);
15635             }
15636         }else{
15637             for(var i = startRow; i >= endRow; i--){
15638                 this.selectRow(i, true);
15639             }
15640         }
15641     },
15642
15643     /**
15644      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15645      * @param {Number} startRow The index of the first row in the range
15646      * @param {Number} endRow The index of the last row in the range
15647      */
15648     deselectRange : function(startRow, endRow, preventViewNotify){
15649         if(this.locked) return;
15650         for(var i = startRow; i <= endRow; i++){
15651             this.deselectRow(i, preventViewNotify);
15652         }
15653     },
15654
15655     /**
15656      * Selects a row.
15657      * @param {Number} row The index of the row to select
15658      * @param {Boolean} keepExisting (optional) True to keep existing selections
15659      */
15660     selectRow : function(index, keepExisting, preventViewNotify){
15661         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15662         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15663             if(!keepExisting || this.singleSelect){
15664                 this.clearSelections();
15665             }
15666             var r = this.grid.dataSource.getAt(index);
15667             this.selections.add(r);
15668             this.last = this.lastActive = index;
15669             if(!preventViewNotify){
15670                 this.grid.getView().onRowSelect(index);
15671             }
15672             this.fireEvent("rowselect", this, index, r);
15673             this.fireEvent("selectionchange", this);
15674         }
15675     },
15676
15677     /**
15678      * Deselects a row.
15679      * @param {Number} row The index of the row to deselect
15680      */
15681     deselectRow : function(index, preventViewNotify){
15682         if(this.locked) return;
15683         if(this.last == index){
15684             this.last = false;
15685         }
15686         if(this.lastActive == index){
15687             this.lastActive = false;
15688         }
15689         var r = this.grid.dataSource.getAt(index);
15690         this.selections.remove(r);
15691         if(!preventViewNotify){
15692             this.grid.getView().onRowDeselect(index);
15693         }
15694         this.fireEvent("rowdeselect", this, index);
15695         this.fireEvent("selectionchange", this);
15696     },
15697
15698     // private
15699     restoreLast : function(){
15700         if(this._last){
15701             this.last = this._last;
15702         }
15703     },
15704
15705     // private
15706     acceptsNav : function(row, col, cm){
15707         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15708     },
15709
15710     // private
15711     onEditorKey : function(field, e){
15712         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15713         if(k == e.TAB){
15714             e.stopEvent();
15715             ed.completeEdit();
15716             if(e.shiftKey){
15717                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15718             }else{
15719                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15720             }
15721         }else if(k == e.ENTER && !e.ctrlKey){
15722             e.stopEvent();
15723             ed.completeEdit();
15724             if(e.shiftKey){
15725                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15726             }else{
15727                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15728             }
15729         }else if(k == e.ESC){
15730             ed.cancelEdit();
15731         }
15732         if(newCell){
15733             g.startEditing(newCell[0], newCell[1]);
15734         }
15735     }
15736 });/*
15737  * - LGPL
15738  *
15739  * element
15740  * 
15741  */
15742
15743 /**
15744  * @class Roo.bootstrap.MessageBar
15745  * @extends Roo.bootstrap.Component
15746  * Bootstrap MessageBar class
15747  * @cfg {String} html contents of the MessageBar
15748  * @cfg {String} weight (info | success | warning | danger) default info
15749  * @cfg {String} beforeClass insert the bar before the given class
15750  * @cfg {Boolean} closable (true | false) default false
15751  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15752  * 
15753  * @constructor
15754  * Create a new Element
15755  * @param {Object} config The config object
15756  */
15757
15758 Roo.bootstrap.MessageBar = function(config){
15759     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15760 };
15761
15762 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15763     
15764     html: '',
15765     weight: 'info',
15766     closable: false,
15767     fixed: false,
15768     beforeClass: 'bootstrap-sticky-wrap',
15769     
15770     getAutoCreate : function(){
15771         
15772         var cfg = {
15773             tag: 'div',
15774             cls: 'alert alert-dismissable alert-' + this.weight,
15775             cn: [
15776                 {
15777                     tag: 'span',
15778                     cls: 'message',
15779                     html: this.html || ''
15780                 }
15781             ]
15782         }
15783         
15784         if(this.fixed){
15785             cfg.cls += ' alert-messages-fixed';
15786         }
15787         
15788         if(this.closable){
15789             cfg.cn.push({
15790                 tag: 'button',
15791                 cls: 'close',
15792                 html: 'x'
15793             });
15794         }
15795         
15796         return cfg;
15797     },
15798     
15799     onRender : function(ct, position)
15800     {
15801         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15802         
15803         if(!this.el){
15804             var cfg = Roo.apply({},  this.getAutoCreate());
15805             cfg.id = Roo.id();
15806             
15807             if (this.cls) {
15808                 cfg.cls += ' ' + this.cls;
15809             }
15810             if (this.style) {
15811                 cfg.style = this.style;
15812             }
15813             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15814             
15815             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15816         }
15817         
15818         this.el.select('>button.close').on('click', this.hide, this);
15819         
15820     },
15821     
15822     show : function()
15823     {
15824         if (!this.rendered) {
15825             this.render();
15826         }
15827         
15828         this.el.show();
15829         
15830         this.fireEvent('show', this);
15831         
15832     },
15833     
15834     hide : function()
15835     {
15836         if (!this.rendered) {
15837             this.render();
15838         }
15839         
15840         this.el.hide();
15841         
15842         this.fireEvent('hide', this);
15843     },
15844     
15845     update : function()
15846     {
15847 //        var e = this.el.dom.firstChild;
15848 //        
15849 //        if(this.closable){
15850 //            e = e.nextSibling;
15851 //        }
15852 //        
15853 //        e.data = this.html || '';
15854
15855         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15856     }
15857    
15858 });
15859
15860  
15861
15862