Roo/bootstrap/Table.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position){
306         
307         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472     
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = this.html || cfg.html;
489         
490         if (this.toggle == true) {
491             cfg={
492                 tag: 'div',
493                 cls: 'slider-frame roo-button',
494                 cn: [
495                     {
496                         tag: 'span',
497                         'data-on-text':'ON',
498                         'data-off-text':'OFF',
499                         cls: 'slider-button',
500                         html: this.offtext
501                     }
502                 ]
503             };
504             
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 cfg.cls += ' '+this.weight;
507             }
508             
509             return cfg;
510         }
511         
512         if (this.isClose) {
513             cfg.cls += ' close';
514             
515             cfg["aria-hidden"] = true;
516             
517             cfg.html = "&times;";
518             
519             return cfg;
520         }
521         
522          
523         if (this.theme==='default') {
524             cfg.cls = 'btn roo-button';
525             
526             //if (this.parentType != 'Navbar') {
527             this.weight = this.weight.length ?  this.weight : 'default';
528             //}
529             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
530                 
531                 cfg.cls += ' btn-' + this.weight;
532             }
533         } else if (this.theme==='glow') {
534             
535             cfg.tag = 'a';
536             cfg.cls = 'btn-glow roo-button';
537             
538             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
539                 
540                 cfg.cls += ' ' + this.weight;
541             }
542         }
543    
544         
545         if (this.inverse) {
546             this.cls += ' inverse';
547         }
548         
549         
550         if (this.active) {
551             cfg.cls += ' active';
552         }
553         
554         if (this.disabled) {
555             cfg.disabled = 'disabled';
556         }
557         
558         if (this.items) {
559             Roo.log('changing to ul' );
560             cfg.tag = 'ul';
561             this.glyphicon = 'caret';
562         }
563         
564         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
565          
566         //gsRoo.log(this.parentType);
567         if (this.parentType === 'Navbar' && !this.parent().bar) {
568             Roo.log('changing to li?');
569             
570             cfg.tag = 'li';
571             
572             cfg.cls = '';
573             cfg.cn =  [{
574                 tag : 'a',
575                 cls : 'roo-button',
576                 html : this.html,
577                 href : this.href || '#'
578             }];
579             if (this.menu) {
580                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
581                 cfg.cls += ' dropdown';
582             }   
583             
584             delete cfg.html;
585             
586         }
587         
588        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
589         
590         if (this.glyphicon) {
591             cfg.html = ' ' + cfg.html;
592             
593             cfg.cn = [
594                 {
595                     tag: 'span',
596                     cls: 'glyphicon glyphicon-' + this.glyphicon
597                 }
598             ];
599         }
600         
601         if (this.badge) {
602             cfg.html += ' ';
603             
604             cfg.tag = 'a';
605             
606 //            cfg.cls='btn roo-button';
607             
608             cfg.href=this.href;
609             
610             var value = cfg.html;
611             
612             if(this.glyphicon){
613                 value = {
614                             tag: 'span',
615                             cls: 'glyphicon glyphicon-' + this.glyphicon,
616                             html: this.html
617                         };
618                 
619             }
620             
621             cfg.cn = [
622                 value,
623                 {
624                     tag: 'span',
625                     cls: 'badge',
626                     html: this.badge
627                 }
628             ];
629             
630             cfg.html='';
631         }
632         
633         if (this.menu) {
634             cfg.cls += ' dropdown';
635             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
636         }
637         
638         if (cfg.tag !== 'a' && this.href !== '') {
639             throw "Tag must be a to set href.";
640         } else if (this.href.length > 0) {
641             cfg.href = this.href;
642         }
643         
644         if(this.removeClass){
645             cfg.cls = '';
646         }
647         
648         if(this.target){
649             cfg.target = this.target;
650         }
651         
652         return cfg;
653     },
654     initEvents: function() {
655        // Roo.log('init events?');
656 //        Roo.log(this.el.dom);
657        if (this.el.hasClass('roo-button')) {
658             this.el.on('click', this.onClick, this);
659        } else {
660             this.el.select('.roo-button').on('click', this.onClick, this);
661        }
662        
663        
664         
665     },
666     onClick : function(e)
667     {
668         if (this.disabled) {
669             return;
670         }
671         
672         Roo.log('button on click ');
673         if(this.preventDefault){
674             e.preventDefault();
675         }
676         if (this.pressed === true || this.pressed === false) {
677             this.pressed = !this.pressed;
678             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679             this.fireEvent('toggle', this, e, this.pressed);
680         }
681         
682         
683         this.fireEvent('click', this, e);
684     },
685     
686     /**
687      * Enables this button
688      */
689     enable : function()
690     {
691         this.disabled = false;
692         this.el.removeClass('disabled');
693     },
694     
695     /**
696      * Disable this button
697      */
698     disable : function()
699     {
700         this.disabled = true;
701         this.el.addClass('disabled');
702     },
703      /**
704      * sets the active state on/off, 
705      * @param {Boolean} state (optional) Force a particular state
706      */
707     setActive : function(v) {
708         
709         this.el[v ? 'addClass' : 'removeClass']('active');
710     },
711      /**
712      * toggles the current active state 
713      */
714     toggleActive : function()
715     {
716        var active = this.el.hasClass('active');
717        this.setActive(!active);
718        
719         
720     }
721     
722     
723     
724 });
725
726  /*
727  * - LGPL
728  *
729  * column
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.Column
735  * @extends Roo.bootstrap.Component
736  * Bootstrap Column class
737  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739  * @cfg {Number} md colspan out of 12 for computer-sized screens
740  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741  * @cfg {String} html content of column.
742  * 
743  * @constructor
744  * Create a new Column
745  * @param {Object} config The config object
746  */
747
748 Roo.bootstrap.Column = function(config){
749     Roo.bootstrap.Column.superclass.constructor.call(this, config);
750 };
751
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
753     
754     xs: null,
755     sm: null,
756     md: null,
757     lg: null,
758     html: '',
759     offset: 0,
760     
761     getAutoCreate : function(){
762         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
763         
764         cfg = {
765             tag: 'div',
766             cls: 'column'
767         };
768         
769         var settings=this;
770         ['xs','sm','md','lg'].map(function(size){
771             if (settings[size]) {
772                 cfg.cls += ' col-' + size + '-' + settings[size];
773             }
774         });
775         if (this.html.length) {
776             cfg.html = this.html;
777         }
778         
779         return cfg;
780     }
781    
782 });
783
784  
785
786  /*
787  * - LGPL
788  *
789  * page container.
790  * 
791  */
792
793
794 /**
795  * @class Roo.bootstrap.Container
796  * @extends Roo.bootstrap.Component
797  * Bootstrap Container class
798  * @cfg {Boolean} jumbotron is it a jumbotron element
799  * @cfg {String} html content of element
800  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802  * @cfg {String} header content of header (for panel)
803  * @cfg {String} footer content of footer (for panel)
804  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
805  *     
806  * @constructor
807  * Create a new Container
808  * @param {Object} config The config object
809  */
810
811 Roo.bootstrap.Container = function(config){
812     Roo.bootstrap.Container.superclass.constructor.call(this, config);
813 };
814
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
816     
817     jumbotron : false,
818     well: '',
819     panel : '',
820     header: '',
821     footer : '',
822     sticky: '',
823   
824      
825     getChildContainer : function() {
826         
827         if(!this.el){
828             return false;
829         }
830         
831         if (this.panel.length) {
832             return this.el.select('.panel-body',true).first();
833         }
834         
835         return this.el;
836     },
837     
838     
839     getAutoCreate : function(){
840         
841         var cfg = {
842             html : '',
843             cls : ''
844         };
845         if (this.jumbotron) {
846             cfg.cls = 'jumbotron';
847         }
848         if (this.cls) {
849             cfg.cls = this.cls + '';
850         }
851         
852         if (this.sticky.length) {
853             
854             var bd = Roo.get(document.body);
855             if (!bd.hasClass('bootstrap-sticky')) {
856                 bd.addClass('bootstrap-sticky');
857                 Roo.select('html',true).setStyle('height', '100%');
858             }
859              
860             cfg.cls += 'bootstrap-sticky-' + this.sticky;
861         }
862         
863         
864         if (this.well.length) {
865             switch (this.well) {
866                 case 'lg':
867                 case 'sm':
868                     cfg.cls +=' well well-' +this.well;
869                     break;
870                 default:
871                     cfg.cls +=' well';
872                     break;
873             }
874         }
875         
876         var body = cfg;
877         
878         if (this.panel.length) {
879             cfg.cls += ' panel panel-' + this.panel;
880             cfg.cn = [];
881             if (this.header.length) {
882                 cfg.cn.push({
883                     
884                     cls : 'panel-heading',
885                     cn : [{
886                         tag: 'h3',
887                         cls : 'panel-title',
888                         html : this.header
889                     }]
890                     
891                 });
892             }
893             body = false;
894             cfg.cn.push({
895                 cls : 'panel-body',
896                 html : this.html
897             });
898             
899             
900             if (this.footer.length) {
901                 cfg.cn.push({
902                     cls : 'panel-footer',
903                     html : this.footer
904                     
905                 });
906             }
907             
908         }
909         if (body) {
910             body.html = this.html || cfg.html;
911         }
912         if (!cfg.cls.length) {
913             cfg.cls =  'container';
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  /*
922  * - LGPL
923  *
924  * image
925  * 
926  */
927
928
929 /**
930  * @class Roo.bootstrap.Img
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Img class
933  * @cfg {Boolean} imgResponsive false | true
934  * @cfg {String} border rounded | circle | thumbnail
935  * @cfg {String} src image source
936  * @cfg {String} alt image alternative text
937  * @cfg {String} href a tag href
938  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
939  * 
940  * @constructor
941  * Create a new Input
942  * @param {Object} config The config object
943  */
944
945 Roo.bootstrap.Img = function(config){
946     Roo.bootstrap.Img.superclass.constructor.call(this, config);
947     
948     this.addEvents({
949         // img events
950         /**
951          * @event click
952          * The img click event for the img.
953          * @param {Roo.EventObject} e
954          */
955         "click" : true
956     });
957 };
958
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
960     
961     imgResponsive: true,
962     border: '',
963     src: '',
964     href: false,
965     target: false,
966
967     getAutoCreate : function(){
968         
969         var cfg = {
970             tag: 'img',
971             cls: 'img-responsive',
972             html : null
973         }
974         
975         cfg.html = this.html || cfg.html;
976         
977         cfg.src = this.src || cfg.src;
978         
979         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980             cfg.cls += ' img-' + this.border;
981         }
982         
983         if(this.alt){
984             cfg.alt = this.alt;
985         }
986         
987         if(this.href){
988             var a = {
989                 tag: 'a',
990                 href: this.href,
991                 cn: [
992                     cfg
993                 ]
994             }
995             
996             if(this.target){
997                 a.target = this.target;
998             }
999             
1000         }
1001         
1002         
1003         return (this.href) ? a : cfg;
1004     },
1005     
1006     initEvents: function() {
1007         
1008         if(!this.href){
1009             this.el.on('click', this.onClick, this);
1010         }
1011     },
1012     
1013     onClick : function(e)
1014     {
1015         Roo.log('img onclick');
1016         this.fireEvent('click', this, e);
1017     }
1018    
1019 });
1020
1021  /*
1022  * - LGPL
1023  *
1024  * header
1025  * 
1026  */
1027
1028 /**
1029  * @class Roo.bootstrap.Header
1030  * @extends Roo.bootstrap.Component
1031  * Bootstrap Header class
1032  * @cfg {String} html content of header
1033  * @cfg {Number} level (1|2|3|4|5|6) default 1
1034  * 
1035  * @constructor
1036  * Create a new Header
1037  * @param {Object} config The config object
1038  */
1039
1040
1041 Roo.bootstrap.Header  = function(config){
1042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1046     
1047     //href : false,
1048     html : false,
1049     level : 1,
1050     
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag: 'h' + (1 *this.level),
1057             html: this.html || 'fill in html'
1058         } ;
1059         
1060         return cfg;
1061     }
1062    
1063 });
1064
1065  
1066
1067  /*
1068  * Based on:
1069  * Ext JS Library 1.1.1
1070  * Copyright(c) 2006-2007, Ext JS, LLC.
1071  *
1072  * Originally Released Under LGPL - original licence link has changed is not relivant.
1073  *
1074  * Fork - LGPL
1075  * <script type="text/javascript">
1076  */
1077  
1078 /**
1079  * @class Roo.bootstrap.MenuMgr
1080  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1081  * @singleton
1082  */
1083 Roo.bootstrap.MenuMgr = function(){
1084    var menus, active, groups = {}, attached = false, lastShow = new Date();
1085
1086    // private - called when first menu is created
1087    function init(){
1088        menus = {};
1089        active = new Roo.util.MixedCollection();
1090        Roo.get(document).addKeyListener(27, function(){
1091            if(active.length > 0){
1092                hideAll();
1093            }
1094        });
1095    }
1096
1097    // private
1098    function hideAll(){
1099        if(active && active.length > 0){
1100            var c = active.clone();
1101            c.each(function(m){
1102                m.hide();
1103            });
1104        }
1105    }
1106
1107    // private
1108    function onHide(m){
1109        active.remove(m);
1110        if(active.length < 1){
1111            Roo.get(document).un("mouseup", onMouseDown);
1112             
1113            attached = false;
1114        }
1115    }
1116
1117    // private
1118    function onShow(m){
1119        var last = active.last();
1120        lastShow = new Date();
1121        active.add(m);
1122        if(!attached){
1123           Roo.get(document).on("mouseup", onMouseDown);
1124            
1125            attached = true;
1126        }
1127        if(m.parentMenu){
1128           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129           m.parentMenu.activeChild = m;
1130        }else if(last && last.isVisible()){
1131           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1132        }
1133    }
1134
1135    // private
1136    function onBeforeHide(m){
1137        if(m.activeChild){
1138            m.activeChild.hide();
1139        }
1140        if(m.autoHideTimer){
1141            clearTimeout(m.autoHideTimer);
1142            delete m.autoHideTimer;
1143        }
1144    }
1145
1146    // private
1147    function onBeforeShow(m){
1148        var pm = m.parentMenu;
1149        if(!pm && !m.allowOtherMenus){
1150            hideAll();
1151        }else if(pm && pm.activeChild && active != m){
1152            pm.activeChild.hide();
1153        }
1154    }
1155
1156    // private
1157    function onMouseDown(e){
1158         Roo.log("on MouseDown");
1159         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1160            hideAll();
1161         }
1162         
1163         
1164    }
1165
1166    // private
1167    function onBeforeCheck(mi, state){
1168        if(state){
1169            var g = groups[mi.group];
1170            for(var i = 0, l = g.length; i < l; i++){
1171                if(g[i] != mi){
1172                    g[i].setChecked(false);
1173                }
1174            }
1175        }
1176    }
1177
1178    return {
1179
1180        /**
1181         * Hides all menus that are currently visible
1182         */
1183        hideAll : function(){
1184             hideAll();  
1185        },
1186
1187        // private
1188        register : function(menu){
1189            if(!menus){
1190                init();
1191            }
1192            menus[menu.id] = menu;
1193            menu.on("beforehide", onBeforeHide);
1194            menu.on("hide", onHide);
1195            menu.on("beforeshow", onBeforeShow);
1196            menu.on("show", onShow);
1197            var g = menu.group;
1198            if(g && menu.events["checkchange"]){
1199                if(!groups[g]){
1200                    groups[g] = [];
1201                }
1202                groups[g].push(menu);
1203                menu.on("checkchange", onCheck);
1204            }
1205        },
1206
1207         /**
1208          * Returns a {@link Roo.menu.Menu} object
1209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210          * be used to generate and return a new Menu instance.
1211          */
1212        get : function(menu){
1213            if(typeof menu == "string"){ // menu id
1214                return menus[menu];
1215            }else if(menu.events){  // menu instance
1216                return menu;
1217            }
1218            /*else if(typeof menu.length == 'number'){ // array of menu items?
1219                return new Roo.bootstrap.Menu({items:menu});
1220            }else{ // otherwise, must be a config
1221                return new Roo.bootstrap.Menu(menu);
1222            }
1223            */
1224            return false;
1225        },
1226
1227        // private
1228        unregister : function(menu){
1229            delete menus[menu.id];
1230            menu.un("beforehide", onBeforeHide);
1231            menu.un("hide", onHide);
1232            menu.un("beforeshow", onBeforeShow);
1233            menu.un("show", onShow);
1234            var g = menu.group;
1235            if(g && menu.events["checkchange"]){
1236                groups[g].remove(menu);
1237                menu.un("checkchange", onCheck);
1238            }
1239        },
1240
1241        // private
1242        registerCheckable : function(menuItem){
1243            var g = menuItem.group;
1244            if(g){
1245                if(!groups[g]){
1246                    groups[g] = [];
1247                }
1248                groups[g].push(menuItem);
1249                menuItem.on("beforecheckchange", onBeforeCheck);
1250            }
1251        },
1252
1253        // private
1254        unregisterCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                groups[g].remove(menuItem);
1258                menuItem.un("beforecheckchange", onBeforeCheck);
1259            }
1260        }
1261    };
1262 }();/*
1263  * - LGPL
1264  *
1265  * menu
1266  * 
1267  */
1268
1269 /**
1270  * @class Roo.bootstrap.Menu
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Menu class - container for MenuItems
1273  * @cfg {String} type type of menu
1274  * 
1275  * @constructor
1276  * Create a new Menu
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Menu = function(config){
1282     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283     if (this.registerMenu) {
1284         Roo.bootstrap.MenuMgr.register(this);
1285     }
1286     this.addEvents({
1287         /**
1288          * @event beforeshow
1289          * Fires before this menu is displayed
1290          * @param {Roo.menu.Menu} this
1291          */
1292         beforeshow : true,
1293         /**
1294          * @event beforehide
1295          * Fires before this menu is hidden
1296          * @param {Roo.menu.Menu} this
1297          */
1298         beforehide : true,
1299         /**
1300          * @event show
1301          * Fires after this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         show : true,
1305         /**
1306          * @event hide
1307          * Fires after this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         hide : true,
1311         /**
1312          * @event click
1313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314          * @param {Roo.menu.Menu} this
1315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316          * @param {Roo.EventObject} e
1317          */
1318         click : true,
1319         /**
1320          * @event mouseover
1321          * Fires when the mouse is hovering over this menu
1322          * @param {Roo.menu.Menu} this
1323          * @param {Roo.EventObject} e
1324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1325          */
1326         mouseover : true,
1327         /**
1328          * @event mouseout
1329          * Fires when the mouse exits this menu
1330          * @param {Roo.menu.Menu} this
1331          * @param {Roo.EventObject} e
1332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1333          */
1334         mouseout : true,
1335         /**
1336          * @event itemclick
1337          * Fires when a menu item contained in this menu is clicked
1338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339          * @param {Roo.EventObject} e
1340          */
1341         itemclick: true
1342     });
1343     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1347     
1348    /// html : false,
1349     //align : '',
1350     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1351     type: false,
1352     /**
1353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1354      */
1355     registerMenu : true,
1356     
1357     menuItems :false, // stores the menu items..
1358     
1359     hidden:true,
1360     
1361     parentMenu : false,
1362     
1363     getChildContainer : function() {
1364         return this.el;  
1365     },
1366     
1367     getAutoCreate : function(){
1368          
1369         //if (['right'].indexOf(this.align)!==-1) {
1370         //    cfg.cn[1].cls += ' pull-right'
1371         //}
1372         var cfg = {
1373             tag : 'ul',
1374             cls : 'dropdown-menu' ,
1375             style : 'z-index:1000'
1376             
1377         }
1378         
1379         if (this.type === 'submenu') {
1380             cfg.cls = 'submenu active'
1381         }
1382         
1383         return cfg;
1384     },
1385     initEvents : function() {
1386         
1387        // Roo.log("ADD event");
1388        // Roo.log(this.triggerEl.dom);
1389         this.triggerEl.on('click', this.onTriggerPress, this);
1390         this.triggerEl.addClass('dropdown-toggle');
1391         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1392
1393         this.el.on("mouseover", this.onMouseOver, this);
1394         this.el.on("mouseout", this.onMouseOut, this);
1395         
1396         
1397     },
1398     findTargetItem : function(e){
1399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1400         if(!t){
1401             return false;
1402         }
1403         //Roo.log(t);         Roo.log(t.id);
1404         if(t && t.id){
1405             //Roo.log(this.menuitems);
1406             return this.menuitems.get(t.id);
1407             
1408             //return this.items.get(t.menuItemId);
1409         }
1410         
1411         return false;
1412     },
1413     onClick : function(e){
1414         Roo.log("menu.onClick");
1415         var t = this.findTargetItem(e);
1416         if(!t){
1417             return;
1418         }
1419         Roo.log(e);
1420         /*
1421         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1422             if(t == this.activeItem && t.shouldDeactivate(e)){
1423                 this.activeItem.deactivate();
1424                 delete this.activeItem;
1425                 return;
1426             }
1427             if(t.canActivate){
1428                 this.setActiveItem(t, true);
1429             }
1430             return;
1431             
1432             
1433         }
1434         */
1435         Roo.log('pass click event');
1436         
1437         t.onClick(e);
1438         
1439         this.fireEvent("click", this, t, e);
1440         
1441         this.hide();
1442     },
1443      onMouseOver : function(e){
1444         var t  = this.findTargetItem(e);
1445         //Roo.log(t);
1446         //if(t){
1447         //    if(t.canActivate && !t.disabled){
1448         //        this.setActiveItem(t, true);
1449         //    }
1450         //}
1451         
1452         this.fireEvent("mouseover", this, e, t);
1453     },
1454     isVisible : function(){
1455         return !this.hidden;
1456     },
1457      onMouseOut : function(e){
1458         var t  = this.findTargetItem(e);
1459         
1460         //if(t ){
1461         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1462         //        this.activeItem.deactivate();
1463         //        delete this.activeItem;
1464         //    }
1465         //}
1466         this.fireEvent("mouseout", this, e, t);
1467     },
1468     
1469     
1470     /**
1471      * Displays this menu relative to another element
1472      * @param {String/HTMLElement/Roo.Element} element The element to align to
1473      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474      * the element (defaults to this.defaultAlign)
1475      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1476      */
1477     show : function(el, pos, parentMenu){
1478         this.parentMenu = parentMenu;
1479         if(!this.el){
1480             this.render();
1481         }
1482         this.fireEvent("beforeshow", this);
1483         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1484     },
1485      /**
1486      * Displays this menu at a specific xy position
1487      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489      */
1490     showAt : function(xy, parentMenu, /* private: */_e){
1491         this.parentMenu = parentMenu;
1492         if(!this.el){
1493             this.render();
1494         }
1495         if(_e !== false){
1496             this.fireEvent("beforeshow", this);
1497             
1498             //xy = this.el.adjustForConstraints(xy);
1499         }
1500         //this.el.setXY(xy);
1501         //this.el.show();
1502         this.hideMenuItems();
1503         this.hidden = false;
1504         this.triggerEl.addClass('open');
1505         this.focus();
1506         this.fireEvent("show", this);
1507     },
1508     
1509     focus : function(){
1510         return;
1511         if(!this.hidden){
1512             this.doFocus.defer(50, this);
1513         }
1514     },
1515
1516     doFocus : function(){
1517         if(!this.hidden){
1518             this.focusEl.focus();
1519         }
1520     },
1521
1522     /**
1523      * Hides this menu and optionally all parent menus
1524      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1525      */
1526     hide : function(deep){
1527         
1528         this.hideMenuItems();
1529         if(this.el && this.isVisible()){
1530             this.fireEvent("beforehide", this);
1531             if(this.activeItem){
1532                 this.activeItem.deactivate();
1533                 this.activeItem = null;
1534             }
1535             this.triggerEl.removeClass('open');;
1536             this.hidden = true;
1537             this.fireEvent("hide", this);
1538         }
1539         if(deep === true && this.parentMenu){
1540             this.parentMenu.hide(true);
1541         }
1542     },
1543     
1544     onTriggerPress  : function(e)
1545     {
1546         
1547         Roo.log('trigger press');
1548         //Roo.log(e.getTarget());
1549        // Roo.log(this.triggerEl.dom);
1550         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1551             return;
1552         }
1553         if (this.isVisible()) {
1554             Roo.log('hide');
1555             this.hide();
1556         } else {
1557             this.show(this.triggerEl, false, false);
1558         }
1559         
1560         
1561     },
1562     
1563          
1564        
1565     
1566     hideMenuItems : function()
1567     {
1568         //$(backdrop).remove()
1569         Roo.select('.open',true).each(function(aa) {
1570             
1571             aa.removeClass('open');
1572           //var parent = getParent($(this))
1573           //var relatedTarget = { relatedTarget: this }
1574           
1575            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576           //if (e.isDefaultPrevented()) return
1577            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1578         })
1579     },
1580     addxtypeChild : function (tree, cntr) {
1581         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1582           
1583         this.menuitems.add(comp);
1584         return comp;
1585
1586     },
1587     getEl : function()
1588     {
1589         Roo.log(this.el);
1590         return this.el;
1591     }
1592 });
1593
1594  
1595  /*
1596  * - LGPL
1597  *
1598  * menu item
1599  * 
1600  */
1601
1602
1603 /**
1604  * @class Roo.bootstrap.MenuItem
1605  * @extends Roo.bootstrap.Component
1606  * Bootstrap MenuItem class
1607  * @cfg {String} html the menu label
1608  * @cfg {String} href the link
1609  * @cfg {Boolean} preventDefault (true | false) default true
1610  * 
1611  * 
1612  * @constructor
1613  * Create a new MenuItem
1614  * @param {Object} config The config object
1615  */
1616
1617
1618 Roo.bootstrap.MenuItem = function(config){
1619     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1620     this.addEvents({
1621         // raw events
1622         /**
1623          * @event click
1624          * The raw click event for the entire grid.
1625          * @param {Roo.EventObject} e
1626          */
1627         "click" : true
1628     });
1629 };
1630
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1632     
1633     href : false,
1634     html : false,
1635     preventDefault: true,
1636     
1637     getAutoCreate : function(){
1638         var cfg= {
1639             tag: 'li',
1640         cls: 'dropdown-menu-item',
1641             cn: [
1642             {
1643                 tag : 'a',
1644                 href : '#',
1645                 html : 'Link'
1646             }
1647             ]
1648     };
1649         
1650         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1652         return cfg;
1653     },
1654     
1655     initEvents: function() {
1656         
1657         //this.el.select('a').on('click', this.onClick, this);
1658         
1659     },
1660     onClick : function(e)
1661     {
1662         Roo.log('item on click ');
1663         //if(this.preventDefault){
1664         //    e.preventDefault();
1665         //}
1666         //this.parent().hideMenuItems();
1667         
1668         this.fireEvent('click', this, e);
1669     },
1670     getEl : function()
1671     {
1672         return this.el;
1673     }
1674 });
1675
1676  
1677
1678  /*
1679  * - LGPL
1680  *
1681  * menu separator
1682  * 
1683  */
1684
1685
1686 /**
1687  * @class Roo.bootstrap.MenuSeparator
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap MenuSeparator class
1690  * 
1691  * @constructor
1692  * Create a new MenuItem
1693  * @param {Object} config The config object
1694  */
1695
1696
1697 Roo.bootstrap.MenuSeparator = function(config){
1698     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1699 };
1700
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1702     
1703     getAutoCreate : function(){
1704         var cfg = {
1705             cls: 'divider',
1706             tag : 'li'
1707         };
1708         
1709         return cfg;
1710     }
1711    
1712 });
1713
1714  
1715
1716  
1717 /*
1718 <div class="modal fade">
1719   <div class="modal-dialog">
1720     <div class="modal-content">
1721       <div class="modal-header">
1722         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1723         <h4 class="modal-title">Modal title</h4>
1724       </div>
1725       <div class="modal-body">
1726         <p>One fine body&hellip;</p>
1727       </div>
1728       <div class="modal-footer">
1729         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730         <button type="button" class="btn btn-primary">Save changes</button>
1731       </div>
1732     </div><!-- /.modal-content -->
1733   </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1735 */
1736 /*
1737  * - LGPL
1738  *
1739  * page contgainer.
1740  * 
1741  */
1742
1743 /**
1744  * @class Roo.bootstrap.Modal
1745  * @extends Roo.bootstrap.Component
1746  * Bootstrap Modal class
1747  * @cfg {String} title Title of dialog
1748  * @cfg {Array} buttons Array of buttons or standard button set..
1749  * 
1750  * @constructor
1751  * Create a new Modal Dialog
1752  * @param {Object} config The config object
1753  */
1754
1755 Roo.bootstrap.Modal = function(config){
1756     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1757     this.addEvents({
1758         // raw events
1759         /**
1760          * @event btnclick
1761          * The raw btnclick event for the button
1762          * @param {Roo.EventObject} e
1763          */
1764         "btnclick" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1769     
1770     title : 'test dialog',
1771    
1772     buttons : false,
1773     
1774     onRender : function(ct, position)
1775     {
1776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1777      
1778         if(!this.el){
1779             var cfg = Roo.apply({},  this.getAutoCreate());
1780             cfg.id = Roo.id();
1781             //if(!cfg.name){
1782             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1783             //}
1784             //if (!cfg.name.length) {
1785             //    delete cfg.name;
1786            // }
1787             if (this.cls) {
1788                 cfg.cls += ' ' + this.cls;
1789             }
1790             if (this.style) {
1791                 cfg.style = this.style;
1792             }
1793             this.el = Roo.get(document.body).createChild(cfg, position);
1794         }
1795         //var type = this.el.dom.type;
1796         
1797         if(this.tabIndex !== undefined){
1798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1799         }
1800         
1801         
1802         
1803         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804         this.maskEl.enableDisplayMode("block");
1805         this.maskEl.hide();
1806         //this.el.addClass("x-dlg-modal");
1807     
1808         if (this.buttons) {
1809             Roo.each(this.buttons, function(bb) {
1810                 b = Roo.apply({}, bb);
1811                 b.xns = b.xns || Roo.bootstrap;
1812                 b.xtype = b.xtype || 'Button';
1813                 if (typeof(b.listeners) == 'undefined') {
1814                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1815                 }
1816                 
1817                 var btn = Roo.factory(b);
1818                 
1819                 btn.onRender(this.el.select('.modal-footer').first());
1820                 
1821             },this);
1822         }
1823         // render the children.
1824         var nitems = [];
1825         
1826         if(typeof(this.items) != 'undefined'){
1827             var items = this.items;
1828             delete this.items;
1829
1830             for(var i =0;i < items.length;i++) {
1831                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1832             }
1833         }
1834         
1835         this.items = nitems;
1836         this.initEvents();
1837         //this.el.addClass([this.fieldClass, this.cls]);
1838         
1839     },
1840     getAutoCreate : function(){
1841         
1842         
1843         var bdy = {
1844                 cls : 'modal-body',
1845                 html : this.html || ''
1846         };
1847         
1848          
1849         return modal = {
1850             cls: "modal fade",
1851             cn : [
1852                 {
1853                     cls: "modal-dialog",
1854                     cn : [
1855                         {
1856                             cls : "modal-content",
1857                             cn : [
1858                                 {
1859                                     cls : 'modal-header',
1860                                     cn : [
1861                                         {
1862                                             tag: 'button',
1863                                             cls : 'close',
1864                                             html : '&times'
1865                                         },
1866                                         {
1867                                             tag: 'h4',
1868                                             cls : 'modal-title',
1869                                             html : this.title
1870                                         }
1871                                     
1872                                     ]
1873                                 },
1874                                 bdy,
1875                                 {
1876                                     cls : 'modal-footer' 
1877                                 }
1878                                 
1879                                 
1880                             ]
1881                             
1882                         }
1883                     ]
1884                         
1885                 }
1886             ]
1887             
1888             
1889         };
1890           
1891     },
1892     getChildContainer : function() {
1893          
1894          return this.el.select('.modal-body',true).first();
1895         
1896     },
1897     getButtonContainer : function() {
1898          return this.el.select('.modal-footer',true).first();
1899         
1900     },
1901     initEvents : function()
1902     {
1903         this.el.select('.modal-header .close').on('click', this.hide, this);
1904 //        
1905 //        this.addxtype(this);
1906     },
1907     show : function() {
1908         
1909         if (!this.rendered) {
1910             this.render();
1911         }
1912        
1913         this.el.addClass('on');
1914         this.el.removeClass('fade');
1915         this.el.setStyle('display', 'block');
1916         Roo.get(document.body).addClass("x-body-masked");
1917         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1918         this.maskEl.show();
1919         this.el.setStyle('zIndex', '10001');
1920         this.fireEvent('show', this);
1921         
1922         
1923     },
1924     hide : function()
1925     {
1926         Roo.log('Modal hide?!');
1927         this.maskEl.hide();
1928         Roo.get(document.body).removeClass("x-body-masked");
1929         this.el.removeClass('on');
1930         this.el.addClass('fade');
1931         this.el.setStyle('display', 'none');
1932         this.fireEvent('hide', this);
1933     },
1934     onButtonClick: function(btn,e)
1935     {
1936         //Roo.log([a,b,c]);
1937         this.fireEvent('btnclick', btn.name, e);
1938     }
1939 });
1940
1941
1942 Roo.apply(Roo.bootstrap.Modal,  {
1943     /**
1944          * Button config that displays a single OK button
1945          * @type Object
1946          */
1947         OK :  [{
1948             name : 'ok',
1949             weight : 'primary',
1950             html : 'OK'
1951         }], 
1952         /**
1953          * Button config that displays Yes and No buttons
1954          * @type Object
1955          */
1956         YESNO : [
1957             {
1958                 name  : 'no',
1959                 html : 'No'
1960             },
1961             {
1962                 name  :'yes',
1963                 weight : 'primary',
1964                 html : 'Yes'
1965             }
1966         ],
1967         
1968         /**
1969          * Button config that displays OK and Cancel buttons
1970          * @type Object
1971          */
1972         OKCANCEL : [
1973             {
1974                name : 'cancel',
1975                 html : 'Cancel'
1976             },
1977             {
1978                 name : 'ok',
1979                 weight : 'primary',
1980                 html : 'OK'
1981             }
1982         ],
1983         /**
1984          * Button config that displays Yes, No and Cancel buttons
1985          * @type Object
1986          */
1987         YESNOCANCEL : [
1988             {
1989                 name : 'yes',
1990                 weight : 'primary',
1991                 html : 'Yes'
1992             },
1993             {
1994                 name : 'no',
1995                 html : 'No'
1996             },
1997             {
1998                 name : 'cancel',
1999                 html : 'Cancel'
2000             }
2001         ]
2002 });
2003  /*
2004  * - LGPL
2005  *
2006  * navbar
2007  * 
2008  */
2009
2010 /**
2011  * @class Roo.bootstrap.Navbar
2012  * @extends Roo.bootstrap.Component
2013  * Bootstrap Navbar class
2014  * @cfg {Boolean} sidebar has side bar
2015  * @cfg {Boolean} bar is a bar?
2016  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017  * @cfg {String} brand what is brand
2018  * @cfg {Boolean} inverse is inverted color
2019  * @cfg {String} type (nav | pills | tabs)
2020  * @cfg {Boolean} arrangement stacked | justified
2021  * @cfg {String} align (left | right) alignment
2022  * @cfg {String} brand_href href of the brand
2023  * @cfg {Boolean} main (true|false) main nav bar? default false
2024  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2025  *
2026  * 
2027  * @constructor
2028  * Create a new Navbar
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.Navbar = function(config){
2034     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2038     
2039     sidebar: false,
2040     
2041     bar: false,
2042     brand: '',
2043     inverse: false,
2044     position: '',
2045     align : false,
2046     type: 'nav',
2047     arrangement: '',
2048     brand_href: false,
2049     main : false,
2050     loadMask : false,
2051     
2052     getAutoCreate : function(){
2053         var cfg = {
2054             cls : 'navbar'
2055         };
2056         
2057         if (this.sidebar === true) {
2058             cfg = {
2059                 tag: 'div',
2060                 cls: 'sidebar-nav'
2061             };
2062             return cfg;
2063         }
2064         
2065         if (this.bar === true) {
2066             cfg = {
2067                 tag: 'nav',
2068                 cls: 'navbar',
2069                 role: 'navigation',
2070                 cn: [
2071                     {
2072                         tag: 'div',
2073                         cls: 'navbar-header',
2074                         cn: [
2075                             {
2076                             tag: 'button',
2077                             type: 'button',
2078                             cls: 'navbar-toggle',
2079                             'data-toggle': 'collapse',
2080                             cn: [
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'sr-only',
2084                                     html: 'Toggle navigation'
2085                                 },
2086                                 {
2087                                     tag: 'span',
2088                                     cls: 'icon-bar'
2089                                 },
2090                                 {
2091                                     tag: 'span',
2092                                     cls: 'icon-bar'
2093                                 },
2094                                 {
2095                                     tag: 'span',
2096                                     cls: 'icon-bar'
2097                                 }
2098                             ]
2099                             }
2100                         ]
2101                     },
2102                     {
2103                     tag: 'div',
2104                     cls: 'collapse navbar-collapse'
2105                     }
2106                 ]
2107             };
2108             
2109             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2110             
2111             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112                 cfg.cls += ' navbar-' + this.position;
2113                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2114             }
2115             
2116             if (this.brand !== '') {
2117                 cfg.cn[0].cn.push({
2118                     tag: 'a',
2119                     href: this.brand_href ? this.brand_href : '#',
2120                     cls: 'navbar-brand',
2121                     cn: [
2122                     this.brand
2123                     ]
2124                 });
2125             }
2126             
2127             if(this.main){
2128                 cfg.cls += ' main-nav';
2129             }
2130             
2131             
2132             return cfg;
2133         
2134         } else if (this.bar === false) {
2135             
2136         } else {
2137             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2138         }
2139         
2140         cfg.cn = [
2141             {
2142                 cls: 'nav',
2143                 tag : 'ul'
2144             }
2145         ];
2146         
2147         if (['tabs','pills'].indexOf(this.type)!==-1) {
2148             cfg.cn[0].cls += ' nav-' + this.type
2149         } else {
2150             if (this.type!=='nav') {
2151             Roo.log('nav type must be nav/tabs/pills')
2152             }
2153             cfg.cn[0].cls += ' navbar-nav'
2154         }
2155         
2156         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157             cfg.cn[0].cls += ' nav-' + this.arrangement;
2158         }
2159         
2160         if (this.align === 'right') {
2161             cfg.cn[0].cls += ' navbar-right';
2162         }
2163         if (this.inverse) {
2164             cfg.cls += ' navbar-inverse';
2165             
2166         }
2167         
2168         
2169         return cfg;
2170     },
2171     
2172     initEvents :function ()
2173     {
2174         //Roo.log(this.el.select('.navbar-toggle',true));
2175         this.el.select('.navbar-toggle',true).on('click', function() {
2176            // Roo.log('click');
2177             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2178         }, this);
2179         
2180         var mark = {
2181             tag: "div",
2182             cls:"x-dlg-mask"
2183         }
2184         
2185         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2186         
2187         var size = this.el.getSize();
2188         this.maskEl.setSize(size.width, size.height);
2189         this.maskEl.enableDisplayMode("block");
2190         this.maskEl.hide();
2191         
2192         if(this.loadMask){
2193             this.maskEl.show();
2194         }
2195     },
2196     
2197     
2198     getChildContainer : function()
2199     {
2200         if (this.bar === true) {
2201             return this.el.select('.collapse',true).first();
2202         }
2203         
2204         return this.el;
2205     },
2206     
2207     mask : function()
2208     {
2209         this.maskEl.show();
2210     },
2211     
2212     unmask : function()
2213     {
2214         this.maskEl.hide();
2215     }
2216    
2217 });
2218
2219  
2220
2221  /*
2222  * - LGPL
2223  *
2224  * nav group
2225  * 
2226  */
2227
2228 /**
2229  * @class Roo.bootstrap.NavGroup
2230  * @extends Roo.bootstrap.Component
2231  * Bootstrap NavGroup class
2232  * @cfg {String} align left | right
2233  * @cfg {Boolean} inverse false | true
2234  * @cfg {String} type (nav|pills|tab) default nav
2235  * 
2236  * @constructor
2237  * Create a new nav group
2238  * @param {Object} config The config object
2239  */
2240
2241 Roo.bootstrap.NavGroup = function(config){
2242     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2243 };
2244
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2246     
2247     align: '',
2248     inverse: false,
2249     form: false,
2250     type: 'nav',
2251     
2252     getAutoCreate : function(){
2253         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2254         
2255         cfg = {
2256             tag : 'ul',
2257             cls: 'nav' 
2258         }
2259         
2260         if (['tabs','pills'].indexOf(this.type)!==-1) {
2261             cfg.cls += ' nav-' + this.type
2262         } else {
2263             if (this.type!=='nav') {
2264                 Roo.log('nav type must be nav/tabs/pills')
2265             }
2266             cfg.cls += ' navbar-nav'
2267         }
2268         
2269         if (this.parent().sidebar === true) {
2270             cfg = {
2271                 tag: 'ul',
2272                 cls: 'dashboard-menu'
2273             }
2274             
2275             return cfg;
2276         }
2277         
2278         if (this.form === true) {
2279             cfg = {
2280                 tag: 'form',
2281                 cls: 'navbar-form'
2282             }
2283             
2284             if (this.align === 'right') {
2285                 cfg.cls += ' navbar-right';
2286             } else {
2287                 cfg.cls += ' navbar-left';
2288             }
2289         }
2290         
2291         if (this.align === 'right') {
2292             cfg.cls += ' navbar-right';
2293         }
2294         
2295         if (this.inverse) {
2296             cfg.cls += ' navbar-inverse';
2297             
2298         }
2299         
2300         
2301         return cfg;
2302     }
2303    
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * row
2312  * 
2313  */
2314
2315 /**
2316  * @class Roo.bootstrap.Navbar.Item
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap Navbar.Button class
2319  * @cfg {String} href  link to
2320  * @cfg {String} html content of button
2321  * @cfg {String} badge text inside badge
2322  * @cfg {String} glyphicon name of glyphicon
2323  * @cfg {String} icon name of font awesome icon
2324  * @cfg {Boolena} active Is item active
2325  * @cfg {Boolean} preventDefault (true | false) default false
2326   
2327  * @constructor
2328  * Create a new Navbar Button
2329  * @param {Object} config The config object
2330  */
2331 Roo.bootstrap.Navbar.Item = function(config){
2332     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2333     this.addEvents({
2334         // raw events
2335         /**
2336          * @event click
2337          * The raw click event for the entire grid.
2338          * @param {Roo.EventObject} e
2339          */
2340         "click" : true
2341     });
2342 };
2343
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2345     
2346     href: false,
2347     html: '',
2348     badge: '',
2349     icon: false,
2350     glyphicon: false,
2351     icon: false,
2352     active: false,
2353     preventDefault : false,
2354     
2355     getAutoCreate : function(){
2356         
2357         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2358         
2359         if (this.parent().parent().sidebar === true) {
2360             cfg = {
2361                 tag: 'li',
2362                 cls: '',
2363                 cn: [
2364                     {
2365                         tag: 'p',
2366                         cls: ''
2367                     }
2368                 ]
2369             }
2370             
2371             if (this.html) {
2372                 cfg.cn[0].html = this.html;
2373             }
2374             
2375             if (this.active) {
2376                 this.cls += ' active';
2377             }
2378             
2379             if (this.menu) {
2380                 cfg.cn[0].cls += ' dropdown-toggle';
2381                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2382             }
2383             
2384             if (this.href) {
2385                 cfg.cn[0].tag = 'a',
2386                 cfg.cn[0].href = this.href;
2387             }
2388             
2389             if (this.glyphicon) {
2390                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2391             }
2392             
2393             if (this.icon) {
2394                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2395             }
2396             
2397             return cfg;
2398         }
2399         
2400         cfg = {
2401             tag: 'li',
2402             cls: 'nav-item'
2403         }
2404         
2405         if (this.active) {
2406             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2407         }
2408             
2409         cfg.cn = [
2410             {
2411                 tag: 'p',
2412                 html: 'Text'
2413             }
2414         ];
2415         
2416         if (this.glyphicon) {
2417             if(cfg.html){cfg.html = ' ' + this.html};
2418             cfg.cn=[
2419                 {
2420                     tag: 'span',
2421                     cls: 'glyphicon glyphicon-' + this.glyphicon
2422                 }
2423             ];
2424         }
2425         
2426         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2427         
2428         if (this.menu) {
2429             cfg.cn[0].tag='a';
2430             cfg.cn[0].href='#';
2431             cfg.cn[0].html += " <span class='caret'></span>";
2432         //}else if (!this.href) {
2433         //    cfg.cn[0].tag='p';
2434         //    cfg.cn[0].cls='navbar-text';
2435         } else {
2436             cfg.cn[0].tag='a';
2437             cfg.cn[0].href=this.href||'#';
2438             cfg.cn[0].html=this.html;
2439         }
2440         
2441         if (this.badge !== '') {
2442             
2443             cfg.cn[0].cn=[
2444                 cfg.cn[0].html + ' ',
2445                 {
2446                     tag: 'span',
2447                     cls: 'badge',
2448                     html: this.badge
2449                 }
2450             ];
2451             cfg.cn[0].html=''
2452         }
2453          
2454         if (this.icon) {
2455             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2456         }
2457         
2458         return cfg;
2459     },
2460     initEvents: function() {
2461        // Roo.log('init events?');
2462        // Roo.log(this.el.dom);
2463         this.el.select('a',true).on('click', this.onClick, this);
2464     },
2465     
2466     onClick : function(e)
2467     {
2468         if(this.preventDefault){
2469             e.preventDefault();
2470         }
2471         
2472         if(this.fireEvent('click', this, e) === false){
2473             return;
2474         };
2475         
2476         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477             this.onTabsClick(e);
2478         } 
2479     },
2480     
2481     onTabsClick : function(e)
2482     {
2483         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484             v.removeClass('active');
2485         })
2486
2487         this.el.addClass('active');
2488
2489         if(this.href && this.href.substring(0,1) == '#'){
2490             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2491
2492             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493                 v.removeClass('active');
2494             });
2495
2496             tab.addClass('active');
2497         }
2498     }
2499    
2500 });
2501  
2502
2503  /*
2504  * - LGPL
2505  *
2506  * row
2507  * 
2508  */
2509
2510 /**
2511  * @class Roo.bootstrap.Row
2512  * @extends Roo.bootstrap.Component
2513  * Bootstrap Row class (contains columns...)
2514  * 
2515  * @constructor
2516  * Create a new Row
2517  * @param {Object} config The config object
2518  */
2519
2520 Roo.bootstrap.Row = function(config){
2521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2525     
2526     getAutoCreate : function(){
2527        return {
2528             cls: 'row clearfix'
2529        };
2530     }
2531     
2532     
2533 });
2534
2535  
2536
2537  /*
2538  * - LGPL
2539  *
2540  * element
2541  * 
2542  */
2543
2544 /**
2545  * @class Roo.bootstrap.Element
2546  * @extends Roo.bootstrap.Component
2547  * Bootstrap Element class
2548  * @cfg {String} html contents of the element
2549  * @cfg {String} tag tag of the element
2550  * @cfg {String} cls class of the element
2551  * 
2552  * @constructor
2553  * Create a new Element
2554  * @param {Object} config The config object
2555  */
2556
2557 Roo.bootstrap.Element = function(config){
2558     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2559 };
2560
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2562     
2563     tag: 'div',
2564     cls: '',
2565     html: '',
2566      
2567     
2568     getAutoCreate : function(){
2569         
2570         var cfg = {
2571             tag: this.tag,
2572             cls: this.cls,
2573             html: this.html
2574         }
2575         
2576         
2577         
2578         return cfg;
2579     }
2580    
2581 });
2582
2583  
2584
2585  /*
2586  * - LGPL
2587  *
2588  * pagination
2589  * 
2590  */
2591
2592 /**
2593  * @class Roo.bootstrap.Pagination
2594  * @extends Roo.bootstrap.Component
2595  * Bootstrap Pagination class
2596  * @cfg {String} size xs | sm | md | lg
2597  * @cfg {Boolean} inverse false | true
2598  * 
2599  * @constructor
2600  * Create a new Pagination
2601  * @param {Object} config The config object
2602  */
2603
2604 Roo.bootstrap.Pagination = function(config){
2605     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2609     
2610     cls: false,
2611     size: false,
2612     inverse: false,
2613     
2614     getAutoCreate : function(){
2615         var cfg = {
2616             tag: 'ul',
2617                 cls: 'pagination'
2618         };
2619         if (this.inverse) {
2620             cfg.cls += ' inverse';
2621         }
2622         if (this.html) {
2623             cfg.html=this.html;
2624         }
2625         if (this.cls) {
2626             cfg.cls += " " + this.cls;
2627         }
2628         return cfg;
2629     }
2630    
2631 });
2632
2633  
2634
2635  /*
2636  * - LGPL
2637  *
2638  * Pagination item
2639  * 
2640  */
2641
2642
2643 /**
2644  * @class Roo.bootstrap.PaginationItem
2645  * @extends Roo.bootstrap.Component
2646  * Bootstrap PaginationItem class
2647  * @cfg {String} html text
2648  * @cfg {String} href the link
2649  * @cfg {Boolean} preventDefault (true | false) default true
2650  * @cfg {Boolean} active (true | false) default false
2651  * 
2652  * 
2653  * @constructor
2654  * Create a new PaginationItem
2655  * @param {Object} config The config object
2656  */
2657
2658
2659 Roo.bootstrap.PaginationItem = function(config){
2660     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2661     this.addEvents({
2662         // raw events
2663         /**
2664          * @event click
2665          * The raw click event for the entire grid.
2666          * @param {Roo.EventObject} e
2667          */
2668         "click" : true
2669     });
2670 };
2671
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2673     
2674     href : false,
2675     html : false,
2676     preventDefault: true,
2677     active : false,
2678     cls : false,
2679     
2680     getAutoCreate : function(){
2681         var cfg= {
2682             tag: 'li',
2683             cn: [
2684                 {
2685                     tag : 'a',
2686                     href : this.href ? this.href : '#',
2687                     html : this.html ? this.html : ''
2688                 }
2689             ]
2690         };
2691         
2692         if(this.cls){
2693             cfg.cls = this.cls;
2694         }
2695         
2696         if(this.active){
2697             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2698         }
2699         
2700         return cfg;
2701     },
2702     
2703     initEvents: function() {
2704         
2705         this.el.on('click', this.onClick, this);
2706         
2707     },
2708     onClick : function(e)
2709     {
2710         Roo.log('PaginationItem on click ');
2711         if(this.preventDefault){
2712             e.preventDefault();
2713         }
2714         
2715         this.fireEvent('click', this, e);
2716     }
2717    
2718 });
2719
2720  
2721
2722  /*
2723  * - LGPL
2724  *
2725  * slider
2726  * 
2727  */
2728
2729
2730 /**
2731  * @class Roo.bootstrap.Slider
2732  * @extends Roo.bootstrap.Component
2733  * Bootstrap Slider class
2734  *    
2735  * @constructor
2736  * Create a new Slider
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.Slider = function(config){
2741     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2745     
2746     getAutoCreate : function(){
2747         
2748         var cfg = {
2749             tag: 'div',
2750             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2751             cn: [
2752                 {
2753                     tag: 'a',
2754                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2755                 }
2756             ]
2757         }
2758         
2759         return cfg;
2760     }
2761    
2762 });
2763
2764  /*
2765  * - LGPL
2766  *
2767  * table
2768  * 
2769  */
2770
2771 /**
2772  * @class Roo.bootstrap.Table
2773  * @extends Roo.bootstrap.Component
2774  * Bootstrap Table class
2775  * @cfg {String} cls table class
2776  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777  * @cfg {String} bgcolor Specifies the background color for a table
2778  * @cfg {Number} border Specifies whether the table cells should have borders or not
2779  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780  * @cfg {Number} cellspacing Specifies the space between cells
2781  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783  * @cfg {String} sortable Specifies that the table should be sortable
2784  * @cfg {String} summary Specifies a summary of the content of a table
2785  * @cfg {Number} width Specifies the width of a table
2786  * 
2787  * @cfg {boolean} striped Should the rows be alternative striped
2788  * @cfg {boolean} bordered Add borders to the table
2789  * @cfg {boolean} hover Add hover highlighting
2790  * @cfg {boolean} condensed Format condensed
2791  * @cfg {boolean} responsive Format condensed
2792  *
2793  
2794  
2795  * 
2796  * @constructor
2797  * Create a new Table
2798  * @param {Object} config The config object
2799  */
2800
2801 Roo.bootstrap.Table = function(config){
2802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2803     
2804     if (this.sm) {
2805         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806         this.sm = this.selModel;
2807         this.sm.xmodule = this.xmodule || false;
2808     }
2809     if (this.cm && typeof(this.cm.config) == 'undefined') {
2810         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811         this.cm = this.colModel;
2812         this.cm.xmodule = this.xmodule || false;
2813     }
2814     if (this.store) {
2815         this.store= Roo.factory(this.store, Roo.data);
2816         this.ds = this.store;
2817         this.ds.xmodule = this.xmodule || false;
2818          
2819     }
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2823     
2824     cls: false,
2825     align: false,
2826     bgcolor: false,
2827     border: false,
2828     cellpadding: false,
2829     cellspacing: false,
2830     frame: false,
2831     rules: false,
2832     sortable: false,
2833     summary: false,
2834     width: false,
2835     striped : false,
2836     bordered: false,
2837     hover:  false,
2838     condensed : false,
2839     responsive : false,
2840     sm : false,
2841     cm : false,
2842     store : false,
2843     
2844     getAutoCreate : function(){
2845         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2846         
2847         cfg = {
2848             tag: 'table',
2849             cls : 'table',
2850             cn : []
2851         }
2852             
2853         if (this.striped) {
2854             cfg.cls += ' table-striped';
2855         }
2856         if (this.hover) {
2857             cfg.cls += ' table-hover';
2858         }
2859         if (this.bordered) {
2860             cfg.cls += ' table-bordered';
2861         }
2862         if (this.condensed) {
2863             cfg.cls += ' table-condensed';
2864         }
2865         if (this.responsive) {
2866             cfg.cls += ' table-responsive';
2867         }
2868         
2869           
2870         
2871         
2872         if (this.cls) {
2873             cfg.cls+=  ' ' +this.cls;
2874         }
2875         
2876         // this lot should be simplifed...
2877         
2878         if (this.align) {
2879             cfg.align=this.align;
2880         }
2881         if (this.bgcolor) {
2882             cfg.bgcolor=this.bgcolor;
2883         }
2884         if (this.border) {
2885             cfg.border=this.border;
2886         }
2887         if (this.cellpadding) {
2888             cfg.cellpadding=this.cellpadding;
2889         }
2890         if (this.cellspacing) {
2891             cfg.cellspacing=this.cellspacing;
2892         }
2893         if (this.frame) {
2894             cfg.frame=this.frame;
2895         }
2896         if (this.rules) {
2897             cfg.rules=this.rules;
2898         }
2899         if (this.sortable) {
2900             cfg.sortable=this.sortable;
2901         }
2902         if (this.summary) {
2903             cfg.summary=this.summary;
2904         }
2905         if (this.width) {
2906             cfg.width=this.width;
2907         }
2908         
2909         if(this.store || this.cm){
2910             cfg.cn.push(this.renderHeader());
2911             cfg.cn.push(this.renderBody());
2912             cfg.cn.push(this.renderFooter());
2913             
2914             cfg.cls+=  ' TableGrid';
2915         }
2916         
2917         return cfg;
2918     },
2919 //    
2920 //    initTableGrid : function()
2921 //    {
2922 //        var cfg = {};
2923 //        
2924 //        var header = {
2925 //            tag: 'thead',
2926 //            cn : []
2927 //        };
2928 //        
2929 //        var cm = this.cm;
2930 //        
2931 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2932 //            header.cn.push({
2933 //                tag: 'th',
2934 //                html: cm.getColumnHeader(i)
2935 //            })
2936 //        }
2937 //        
2938 //        cfg.push(header);
2939 //        
2940 //        return cfg;
2941 //        
2942 //        
2943 //    },
2944     
2945     initEvents : function()
2946     {   
2947         if(!this.store || !this.cm){
2948             return;
2949         }
2950         
2951         Roo.log('initEvents with ds!!!!');
2952         
2953 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2954 //        this.maskEl.enableDisplayMode("block");
2955 //        this.maskEl.show();
2956         
2957         this.store.on('load', this.onLoad, this);
2958         this.store.on('beforeload', this.onBeforeLoad, this);
2959         
2960         this.store.load();
2961         
2962         
2963         
2964     },
2965     
2966     renderHeader : function()
2967     {
2968         var header = {
2969             tag: 'thead',
2970             cn : []
2971         };
2972         
2973         var cm = this.cm;
2974         
2975         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2976             var index = cm.getDataIndex(i);
2977             header.cn.push({
2978                 tag: 'th',
2979                 sort: index,
2980                 html: cm.getColumnHeader(i)
2981             })
2982         }
2983         
2984         return header;
2985     },
2986     
2987     renderBody : function()
2988     {
2989         var body = {
2990             tag: 'tbody',
2991             cn : []
2992         };
2993         
2994         return body;
2995     },
2996     
2997     renderFooter : function()
2998     {
2999         var footer = {
3000             tag: 'tfoot',
3001             cn : []
3002         };
3003         
3004         return footer;
3005     },
3006     
3007     onLoad : function()
3008     {
3009         Roo.log('ds onload');
3010         
3011         var cm = this.cm;
3012         
3013         var tbody = this.el.select('tbody', true).first();
3014         
3015         var renders = [];
3016         
3017         if(this.store.getCount() > 0){
3018             this.store.data.each(function(d){
3019                 var row = {
3020                     tag : 'tr',
3021                     cn : []
3022                 };
3023                 
3024                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3025                     var renderer = cm.getRenderer(i);
3026                     var config = cm.config[i];
3027                     var value = '';
3028                     var id = Roo.id();
3029                     
3030                     if(typeof(renderer) !== 'undefined'){
3031                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3032                     }
3033                     
3034                     if(typeof(value) === 'object'){
3035                         renders.push({
3036                             id : id,
3037                             cfg : value 
3038                         })
3039                     }
3040                     
3041                     var td = {
3042                         tag: 'td',
3043                         id: id,
3044                         html: (typeof(value) === 'object') ? '' : value
3045                     };
3046                     
3047                     if(typeof(config.width) != 'undefined'){
3048                         td.width = config.width;
3049                     }
3050                     
3051                     row.cn.push(td);
3052                    
3053                 }
3054                 
3055                 tbody.createChild(row);
3056                 
3057             });
3058         }
3059         
3060         
3061         if(renders.length){
3062             var _this = this;
3063             Roo.each(renders, function(r){
3064                 _this.renderColumn(r);
3065             })
3066         }
3067 //        
3068 //        if(this.loadMask){
3069 //            this.maskEl.hide();
3070 //        }
3071     },
3072     
3073     onBeforeLoad : function()
3074     {
3075         Roo.log('ds onBeforeLoad');
3076         
3077         this.clear();
3078         
3079 //        if(this.loadMask){
3080 //            this.maskEl.show();
3081 //        }
3082     },
3083     
3084     clear : function()
3085     {
3086         this.el.select('tbody', true).first().dom.innerHTML = '';
3087     },
3088     
3089     getSelectionModel : function(){
3090         if(!this.selModel){
3091             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3092         }
3093         return this.selModel;
3094     },
3095     
3096     renderColumn : function(r)
3097     {
3098         var _this = this;
3099         r.cfg.render(Roo.get(r.id));
3100         
3101         if(r.cfg.cn){
3102             Roo.each(r.cfg.cn, function(c){
3103                 var child = {
3104                     id: r.id,
3105                     cfg: c
3106                 }
3107                 _this.renderColumn(child);
3108             })
3109         }
3110     }
3111    
3112 });
3113
3114  
3115
3116  /*
3117  * - LGPL
3118  *
3119  * table cell
3120  * 
3121  */
3122
3123 /**
3124  * @class Roo.bootstrap.TableCell
3125  * @extends Roo.bootstrap.Component
3126  * Bootstrap TableCell class
3127  * @cfg {String} html cell contain text
3128  * @cfg {String} cls cell class
3129  * @cfg {String} tag cell tag (td|th) default td
3130  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3131  * @cfg {String} align Aligns the content in a cell
3132  * @cfg {String} axis Categorizes cells
3133  * @cfg {String} bgcolor Specifies the background color of a cell
3134  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3135  * @cfg {Number} colspan Specifies the number of columns a cell should span
3136  * @cfg {String} headers Specifies one or more header cells a cell is related to
3137  * @cfg {Number} height Sets the height of a cell
3138  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3139  * @cfg {Number} rowspan Sets the number of rows a cell should span
3140  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3141  * @cfg {String} valign Vertical aligns the content in a cell
3142  * @cfg {Number} width Specifies the width of a cell
3143  * 
3144  * @constructor
3145  * Create a new TableCell
3146  * @param {Object} config The config object
3147  */
3148
3149 Roo.bootstrap.TableCell = function(config){
3150     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3151 };
3152
3153 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3154     
3155     html: false,
3156     cls: false,
3157     tag: false,
3158     abbr: false,
3159     align: false,
3160     axis: false,
3161     bgcolor: false,
3162     charoff: false,
3163     colspan: false,
3164     headers: false,
3165     height: false,
3166     nowrap: false,
3167     rowspan: false,
3168     scope: false,
3169     valign: false,
3170     width: false,
3171     
3172     
3173     getAutoCreate : function(){
3174         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3175         
3176         cfg = {
3177             tag: 'td'
3178         }
3179         
3180         if(this.tag){
3181             cfg.tag = this.tag;
3182         }
3183         
3184         if (this.html) {
3185             cfg.html=this.html
3186         }
3187         if (this.cls) {
3188             cfg.cls=this.cls
3189         }
3190         if (this.abbr) {
3191             cfg.abbr=this.abbr
3192         }
3193         if (this.align) {
3194             cfg.align=this.align
3195         }
3196         if (this.axis) {
3197             cfg.axis=this.axis
3198         }
3199         if (this.bgcolor) {
3200             cfg.bgcolor=this.bgcolor
3201         }
3202         if (this.charoff) {
3203             cfg.charoff=this.charoff
3204         }
3205         if (this.colspan) {
3206             cfg.colspan=this.colspan
3207         }
3208         if (this.headers) {
3209             cfg.headers=this.headers
3210         }
3211         if (this.height) {
3212             cfg.height=this.height
3213         }
3214         if (this.nowrap) {
3215             cfg.nowrap=this.nowrap
3216         }
3217         if (this.rowspan) {
3218             cfg.rowspan=this.rowspan
3219         }
3220         if (this.scope) {
3221             cfg.scope=this.scope
3222         }
3223         if (this.valign) {
3224             cfg.valign=this.valign
3225         }
3226         if (this.width) {
3227             cfg.width=this.width
3228         }
3229         
3230         
3231         return cfg;
3232     }
3233    
3234 });
3235
3236  
3237
3238  /*
3239  * - LGPL
3240  *
3241  * table row
3242  * 
3243  */
3244
3245 /**
3246  * @class Roo.bootstrap.TableRow
3247  * @extends Roo.bootstrap.Component
3248  * Bootstrap TableRow class
3249  * @cfg {String} cls row class
3250  * @cfg {String} align Aligns the content in a table row
3251  * @cfg {String} bgcolor Specifies a background color for a table row
3252  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3253  * @cfg {String} valign Vertical aligns the content in a table row
3254  * 
3255  * @constructor
3256  * Create a new TableRow
3257  * @param {Object} config The config object
3258  */
3259
3260 Roo.bootstrap.TableRow = function(config){
3261     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3262 };
3263
3264 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3265     
3266     cls: false,
3267     align: false,
3268     bgcolor: false,
3269     charoff: false,
3270     valign: false,
3271     
3272     getAutoCreate : function(){
3273         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3274         
3275         cfg = {
3276             tag: 'tr'
3277         }
3278             
3279         if(this.cls){
3280             cfg.cls = this.cls;
3281         }
3282         if(this.align){
3283             cfg.align = this.align;
3284         }
3285         if(this.bgcolor){
3286             cfg.bgcolor = this.bgcolor;
3287         }
3288         if(this.charoff){
3289             cfg.charoff = this.charoff;
3290         }
3291         if(this.valign){
3292             cfg.valign = this.valign;
3293         }
3294         
3295         return cfg;
3296     }
3297    
3298 });
3299
3300  
3301
3302  /*
3303  * - LGPL
3304  *
3305  * table body
3306  * 
3307  */
3308
3309 /**
3310  * @class Roo.bootstrap.TableBody
3311  * @extends Roo.bootstrap.Component
3312  * Bootstrap TableBody class
3313  * @cfg {String} cls element class
3314  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3315  * @cfg {String} align Aligns the content inside the element
3316  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3317  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3318  * 
3319  * @constructor
3320  * Create a new TableBody
3321  * @param {Object} config The config object
3322  */
3323
3324 Roo.bootstrap.TableBody = function(config){
3325     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3326 };
3327
3328 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3329     
3330     cls: false,
3331     tag: false,
3332     align: false,
3333     charoff: false,
3334     valign: false,
3335     
3336     getAutoCreate : function(){
3337         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3338         
3339         cfg = {
3340             tag: 'tbody'
3341         }
3342             
3343         if (this.cls) {
3344             cfg.cls=this.cls
3345         }
3346         if(this.tag){
3347             cfg.tag = this.tag;
3348         }
3349         
3350         if(this.align){
3351             cfg.align = this.align;
3352         }
3353         if(this.charoff){
3354             cfg.charoff = this.charoff;
3355         }
3356         if(this.valign){
3357             cfg.valign = this.valign;
3358         }
3359         
3360         return cfg;
3361     }
3362     
3363     
3364 //    initEvents : function()
3365 //    {
3366 //        
3367 //        if(!this.store){
3368 //            return;
3369 //        }
3370 //        
3371 //        this.store = Roo.factory(this.store, Roo.data);
3372 //        this.store.on('load', this.onLoad, this);
3373 //        
3374 //        this.store.load();
3375 //        
3376 //    },
3377 //    
3378 //    onLoad: function () 
3379 //    {   
3380 //        this.fireEvent('load', this);
3381 //    }
3382 //    
3383 //   
3384 });
3385
3386  
3387
3388  /*
3389  * Based on:
3390  * Ext JS Library 1.1.1
3391  * Copyright(c) 2006-2007, Ext JS, LLC.
3392  *
3393  * Originally Released Under LGPL - original licence link has changed is not relivant.
3394  *
3395  * Fork - LGPL
3396  * <script type="text/javascript">
3397  */
3398
3399 // as we use this in bootstrap.
3400 Roo.namespace('Roo.form');
3401  /**
3402  * @class Roo.form.Action
3403  * Internal Class used to handle form actions
3404  * @constructor
3405  * @param {Roo.form.BasicForm} el The form element or its id
3406  * @param {Object} config Configuration options
3407  */
3408
3409  
3410  
3411 // define the action interface
3412 Roo.form.Action = function(form, options){
3413     this.form = form;
3414     this.options = options || {};
3415 };
3416 /**
3417  * Client Validation Failed
3418  * @const 
3419  */
3420 Roo.form.Action.CLIENT_INVALID = 'client';
3421 /**
3422  * Server Validation Failed
3423  * @const 
3424  */
3425 Roo.form.Action.SERVER_INVALID = 'server';
3426  /**
3427  * Connect to Server Failed
3428  * @const 
3429  */
3430 Roo.form.Action.CONNECT_FAILURE = 'connect';
3431 /**
3432  * Reading Data from Server Failed
3433  * @const 
3434  */
3435 Roo.form.Action.LOAD_FAILURE = 'load';
3436
3437 Roo.form.Action.prototype = {
3438     type : 'default',
3439     failureType : undefined,
3440     response : undefined,
3441     result : undefined,
3442
3443     // interface method
3444     run : function(options){
3445
3446     },
3447
3448     // interface method
3449     success : function(response){
3450
3451     },
3452
3453     // interface method
3454     handleResponse : function(response){
3455
3456     },
3457
3458     // default connection failure
3459     failure : function(response){
3460         
3461         this.response = response;
3462         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3463         this.form.afterAction(this, false);
3464     },
3465
3466     processResponse : function(response){
3467         this.response = response;
3468         if(!response.responseText){
3469             return true;
3470         }
3471         this.result = this.handleResponse(response);
3472         return this.result;
3473     },
3474
3475     // utility functions used internally
3476     getUrl : function(appendParams){
3477         var url = this.options.url || this.form.url || this.form.el.dom.action;
3478         if(appendParams){
3479             var p = this.getParams();
3480             if(p){
3481                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3482             }
3483         }
3484         return url;
3485     },
3486
3487     getMethod : function(){
3488         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3489     },
3490
3491     getParams : function(){
3492         var bp = this.form.baseParams;
3493         var p = this.options.params;
3494         if(p){
3495             if(typeof p == "object"){
3496                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3497             }else if(typeof p == 'string' && bp){
3498                 p += '&' + Roo.urlEncode(bp);
3499             }
3500         }else if(bp){
3501             p = Roo.urlEncode(bp);
3502         }
3503         return p;
3504     },
3505
3506     createCallback : function(){
3507         return {
3508             success: this.success,
3509             failure: this.failure,
3510             scope: this,
3511             timeout: (this.form.timeout*1000),
3512             upload: this.form.fileUpload ? this.success : undefined
3513         };
3514     }
3515 };
3516
3517 Roo.form.Action.Submit = function(form, options){
3518     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3519 };
3520
3521 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3522     type : 'submit',
3523
3524     haveProgress : false,
3525     uploadComplete : false,
3526     
3527     // uploadProgress indicator.
3528     uploadProgress : function()
3529     {
3530         if (!this.form.progressUrl) {
3531             return;
3532         }
3533         
3534         if (!this.haveProgress) {
3535             Roo.MessageBox.progress("Uploading", "Uploading");
3536         }
3537         if (this.uploadComplete) {
3538            Roo.MessageBox.hide();
3539            return;
3540         }
3541         
3542         this.haveProgress = true;
3543    
3544         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3545         
3546         var c = new Roo.data.Connection();
3547         c.request({
3548             url : this.form.progressUrl,
3549             params: {
3550                 id : uid
3551             },
3552             method: 'GET',
3553             success : function(req){
3554                //console.log(data);
3555                 var rdata = false;
3556                 var edata;
3557                 try  {
3558                    rdata = Roo.decode(req.responseText)
3559                 } catch (e) {
3560                     Roo.log("Invalid data from server..");
3561                     Roo.log(edata);
3562                     return;
3563                 }
3564                 if (!rdata || !rdata.success) {
3565                     Roo.log(rdata);
3566                     Roo.MessageBox.alert(Roo.encode(rdata));
3567                     return;
3568                 }
3569                 var data = rdata.data;
3570                 
3571                 if (this.uploadComplete) {
3572                    Roo.MessageBox.hide();
3573                    return;
3574                 }
3575                    
3576                 if (data){
3577                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3578                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3579                     );
3580                 }
3581                 this.uploadProgress.defer(2000,this);
3582             },
3583        
3584             failure: function(data) {
3585                 Roo.log('progress url failed ');
3586                 Roo.log(data);
3587             },
3588             scope : this
3589         });
3590            
3591     },
3592     
3593     
3594     run : function()
3595     {
3596         // run get Values on the form, so it syncs any secondary forms.
3597         this.form.getValues();
3598         
3599         var o = this.options;
3600         var method = this.getMethod();
3601         var isPost = method == 'POST';
3602         if(o.clientValidation === false || this.form.isValid()){
3603             
3604             if (this.form.progressUrl) {
3605                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3606                     (new Date() * 1) + '' + Math.random());
3607                     
3608             } 
3609             
3610             
3611             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3612                 form:this.form.el.dom,
3613                 url:this.getUrl(!isPost),
3614                 method: method,
3615                 params:isPost ? this.getParams() : null,
3616                 isUpload: this.form.fileUpload
3617             }));
3618             
3619             this.uploadProgress();
3620
3621         }else if (o.clientValidation !== false){ // client validation failed
3622             this.failureType = Roo.form.Action.CLIENT_INVALID;
3623             this.form.afterAction(this, false);
3624         }
3625     },
3626
3627     success : function(response)
3628     {
3629         this.uploadComplete= true;
3630         if (this.haveProgress) {
3631             Roo.MessageBox.hide();
3632         }
3633         
3634         
3635         var result = this.processResponse(response);
3636         if(result === true || result.success){
3637             this.form.afterAction(this, true);
3638             return;
3639         }
3640         if(result.errors){
3641             this.form.markInvalid(result.errors);
3642             this.failureType = Roo.form.Action.SERVER_INVALID;
3643         }
3644         this.form.afterAction(this, false);
3645     },
3646     failure : function(response)
3647     {
3648         this.uploadComplete= true;
3649         if (this.haveProgress) {
3650             Roo.MessageBox.hide();
3651         }
3652         
3653         this.response = response;
3654         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3655         this.form.afterAction(this, false);
3656     },
3657     
3658     handleResponse : function(response){
3659         if(this.form.errorReader){
3660             var rs = this.form.errorReader.read(response);
3661             var errors = [];
3662             if(rs.records){
3663                 for(var i = 0, len = rs.records.length; i < len; i++) {
3664                     var r = rs.records[i];
3665                     errors[i] = r.data;
3666                 }
3667             }
3668             if(errors.length < 1){
3669                 errors = null;
3670             }
3671             return {
3672                 success : rs.success,
3673                 errors : errors
3674             };
3675         }
3676         var ret = false;
3677         try {
3678             ret = Roo.decode(response.responseText);
3679         } catch (e) {
3680             ret = {
3681                 success: false,
3682                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3683                 errors : []
3684             };
3685         }
3686         return ret;
3687         
3688     }
3689 });
3690
3691
3692 Roo.form.Action.Load = function(form, options){
3693     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3694     this.reader = this.form.reader;
3695 };
3696
3697 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3698     type : 'load',
3699
3700     run : function(){
3701         
3702         Roo.Ajax.request(Roo.apply(
3703                 this.createCallback(), {
3704                     method:this.getMethod(),
3705                     url:this.getUrl(false),
3706                     params:this.getParams()
3707         }));
3708     },
3709
3710     success : function(response){
3711         
3712         var result = this.processResponse(response);
3713         if(result === true || !result.success || !result.data){
3714             this.failureType = Roo.form.Action.LOAD_FAILURE;
3715             this.form.afterAction(this, false);
3716             return;
3717         }
3718         this.form.clearInvalid();
3719         this.form.setValues(result.data);
3720         this.form.afterAction(this, true);
3721     },
3722
3723     handleResponse : function(response){
3724         if(this.form.reader){
3725             var rs = this.form.reader.read(response);
3726             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3727             return {
3728                 success : rs.success,
3729                 data : data
3730             };
3731         }
3732         return Roo.decode(response.responseText);
3733     }
3734 });
3735
3736 Roo.form.Action.ACTION_TYPES = {
3737     'load' : Roo.form.Action.Load,
3738     'submit' : Roo.form.Action.Submit
3739 };/*
3740  * - LGPL
3741  *
3742  * form
3743  * 
3744  */
3745
3746 /**
3747  * @class Roo.bootstrap.Form
3748  * @extends Roo.bootstrap.Component
3749  * Bootstrap Form class
3750  * @cfg {String} method  GET | POST (default POST)
3751  * @cfg {String} labelAlign top | left (default top)
3752   * @cfg {String} align left  | right - for navbars
3753
3754  * 
3755  * @constructor
3756  * Create a new Form
3757  * @param {Object} config The config object
3758  */
3759
3760
3761 Roo.bootstrap.Form = function(config){
3762     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3763     this.addEvents({
3764         /**
3765          * @event clientvalidation
3766          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3767          * @param {Form} this
3768          * @param {Boolean} valid true if the form has passed client-side validation
3769          */
3770         clientvalidation: true,
3771         /**
3772          * @event beforeaction
3773          * Fires before any action is performed. Return false to cancel the action.
3774          * @param {Form} this
3775          * @param {Action} action The action to be performed
3776          */
3777         beforeaction: true,
3778         /**
3779          * @event actionfailed
3780          * Fires when an action fails.
3781          * @param {Form} this
3782          * @param {Action} action The action that failed
3783          */
3784         actionfailed : true,
3785         /**
3786          * @event actioncomplete
3787          * Fires when an action is completed.
3788          * @param {Form} this
3789          * @param {Action} action The action that completed
3790          */
3791         actioncomplete : true
3792     });
3793     
3794 };
3795
3796 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3797       
3798      /**
3799      * @cfg {String} method
3800      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3801      */
3802     method : 'POST',
3803     /**
3804      * @cfg {String} url
3805      * The URL to use for form actions if one isn't supplied in the action options.
3806      */
3807     /**
3808      * @cfg {Boolean} fileUpload
3809      * Set to true if this form is a file upload.
3810      */
3811      
3812     /**
3813      * @cfg {Object} baseParams
3814      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3815      */
3816       
3817     /**
3818      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3819      */
3820     timeout: 30,
3821     /**
3822      * @cfg {Sting} align (left|right) for navbar forms
3823      */
3824     align : 'left',
3825
3826     // private
3827     activeAction : null,
3828  
3829     /**
3830      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3831      * element by passing it or its id or mask the form itself by passing in true.
3832      * @type Mixed
3833      */
3834     waitMsgTarget : false,
3835     
3836      
3837     
3838     /**
3839      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3840      * element by passing it or its id or mask the form itself by passing in true.
3841      * @type Mixed
3842      */
3843     
3844     getAutoCreate : function(){
3845         
3846         var cfg = {
3847             tag: 'form',
3848             method : this.method || 'POST',
3849             id : this.id || Roo.id(),
3850             cls : ''
3851         }
3852         if (this.parent().xtype.match(/^Nav/)) {
3853             cfg.cls = 'navbar-form navbar-' + this.align;
3854             
3855         }
3856         
3857         if (this.labelAlign == 'left' ) {
3858             cfg.cls += ' form-horizontal';
3859         }
3860         
3861         
3862         return cfg;
3863     },
3864     initEvents : function()
3865     {
3866         this.el.on('submit', this.onSubmit, this);
3867         
3868         
3869     },
3870     // private
3871     onSubmit : function(e){
3872         e.stopEvent();
3873     },
3874     
3875      /**
3876      * Returns true if client-side validation on the form is successful.
3877      * @return Boolean
3878      */
3879     isValid : function(){
3880         var items = this.getItems();
3881         var valid = true;
3882         items.each(function(f){
3883            if(!f.validate()){
3884                valid = false;
3885                
3886            }
3887         });
3888         return valid;
3889     },
3890     /**
3891      * Returns true if any fields in this form have changed since their original load.
3892      * @return Boolean
3893      */
3894     isDirty : function(){
3895         var dirty = false;
3896         var items = this.getItems();
3897         items.each(function(f){
3898            if(f.isDirty()){
3899                dirty = true;
3900                return false;
3901            }
3902            return true;
3903         });
3904         return dirty;
3905     },
3906      /**
3907      * Performs a predefined action (submit or load) or custom actions you define on this form.
3908      * @param {String} actionName The name of the action type
3909      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3910      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3911      * accept other config options):
3912      * <pre>
3913 Property          Type             Description
3914 ----------------  ---------------  ----------------------------------------------------------------------------------
3915 url               String           The url for the action (defaults to the form's url)
3916 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3917 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3918 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3919                                    validate the form on the client (defaults to false)
3920      * </pre>
3921      * @return {BasicForm} this
3922      */
3923     doAction : function(action, options){
3924         if(typeof action == 'string'){
3925             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3926         }
3927         if(this.fireEvent('beforeaction', this, action) !== false){
3928             this.beforeAction(action);
3929             action.run.defer(100, action);
3930         }
3931         return this;
3932     },
3933     
3934     // private
3935     beforeAction : function(action){
3936         var o = action.options;
3937         
3938         // not really supported yet.. ??
3939         
3940         //if(this.waitMsgTarget === true){
3941             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3942         //}else if(this.waitMsgTarget){
3943         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3944         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3945         //}else {
3946         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3947        // }
3948          
3949     },
3950
3951     // private
3952     afterAction : function(action, success){
3953         this.activeAction = null;
3954         var o = action.options;
3955         
3956         //if(this.waitMsgTarget === true){
3957             this.el.unmask();
3958         //}else if(this.waitMsgTarget){
3959         //    this.waitMsgTarget.unmask();
3960         //}else{
3961         //    Roo.MessageBox.updateProgress(1);
3962         //    Roo.MessageBox.hide();
3963        // }
3964         // 
3965         if(success){
3966             if(o.reset){
3967                 this.reset();
3968             }
3969             Roo.callback(o.success, o.scope, [this, action]);
3970             this.fireEvent('actioncomplete', this, action);
3971             
3972         }else{
3973             
3974             // failure condition..
3975             // we have a scenario where updates need confirming.
3976             // eg. if a locking scenario exists..
3977             // we look for { errors : { needs_confirm : true }} in the response.
3978             if (
3979                 (typeof(action.result) != 'undefined')  &&
3980                 (typeof(action.result.errors) != 'undefined')  &&
3981                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3982            ){
3983                 var _t = this;
3984                 Roo.log("not supported yet");
3985                  /*
3986                 
3987                 Roo.MessageBox.confirm(
3988                     "Change requires confirmation",
3989                     action.result.errorMsg,
3990                     function(r) {
3991                         if (r != 'yes') {
3992                             return;
3993                         }
3994                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
3995                     }
3996                     
3997                 );
3998                 */
3999                 
4000                 
4001                 return;
4002             }
4003             
4004             Roo.callback(o.failure, o.scope, [this, action]);
4005             // show an error message if no failed handler is set..
4006             if (!this.hasListener('actionfailed')) {
4007                 Roo.log("need to add dialog support");
4008                 /*
4009                 Roo.MessageBox.alert("Error",
4010                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4011                         action.result.errorMsg :
4012                         "Saving Failed, please check your entries or try again"
4013                 );
4014                 */
4015             }
4016             
4017             this.fireEvent('actionfailed', this, action);
4018         }
4019         
4020     },
4021     /**
4022      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4023      * @param {String} id The value to search for
4024      * @return Field
4025      */
4026     findField : function(id){
4027         var items = this.getItems();
4028         var field = items.get(id);
4029         if(!field){
4030              items.each(function(f){
4031                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4032                     field = f;
4033                     return false;
4034                 }
4035                 return true;
4036             });
4037         }
4038         return field || null;
4039     },
4040      /**
4041      * Mark fields in this form invalid in bulk.
4042      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4043      * @return {BasicForm} this
4044      */
4045     markInvalid : function(errors){
4046         if(errors instanceof Array){
4047             for(var i = 0, len = errors.length; i < len; i++){
4048                 var fieldError = errors[i];
4049                 var f = this.findField(fieldError.id);
4050                 if(f){
4051                     f.markInvalid(fieldError.msg);
4052                 }
4053             }
4054         }else{
4055             var field, id;
4056             for(id in errors){
4057                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4058                     field.markInvalid(errors[id]);
4059                 }
4060             }
4061         }
4062         //Roo.each(this.childForms || [], function (f) {
4063         //    f.markInvalid(errors);
4064         //});
4065         
4066         return this;
4067     },
4068
4069     /**
4070      * Set values for fields in this form in bulk.
4071      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4072      * @return {BasicForm} this
4073      */
4074     setValues : function(values){
4075         if(values instanceof Array){ // array of objects
4076             for(var i = 0, len = values.length; i < len; i++){
4077                 var v = values[i];
4078                 var f = this.findField(v.id);
4079                 if(f){
4080                     f.setValue(v.value);
4081                     if(this.trackResetOnLoad){
4082                         f.originalValue = f.getValue();
4083                     }
4084                 }
4085             }
4086         }else{ // object hash
4087             var field, id;
4088             for(id in values){
4089                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4090                     
4091                     if (field.setFromData && 
4092                         field.valueField && 
4093                         field.displayField &&
4094                         // combos' with local stores can 
4095                         // be queried via setValue()
4096                         // to set their value..
4097                         (field.store && !field.store.isLocal)
4098                         ) {
4099                         // it's a combo
4100                         var sd = { };
4101                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4102                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4103                         field.setFromData(sd);
4104                         
4105                     } else {
4106                         field.setValue(values[id]);
4107                     }
4108                     
4109                     
4110                     if(this.trackResetOnLoad){
4111                         field.originalValue = field.getValue();
4112                     }
4113                 }
4114             }
4115         }
4116          
4117         //Roo.each(this.childForms || [], function (f) {
4118         //    f.setValues(values);
4119         //});
4120                 
4121         return this;
4122     },
4123
4124     /**
4125      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4126      * they are returned as an array.
4127      * @param {Boolean} asString
4128      * @return {Object}
4129      */
4130     getValues : function(asString){
4131         //if (this.childForms) {
4132             // copy values from the child forms
4133         //    Roo.each(this.childForms, function (f) {
4134         //        this.setValues(f.getValues());
4135         //    }, this);
4136         //}
4137         
4138         
4139         
4140         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4141         if(asString === true){
4142             return fs;
4143         }
4144         return Roo.urlDecode(fs);
4145     },
4146     
4147     /**
4148      * Returns the fields in this form as an object with key/value pairs. 
4149      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4150      * @return {Object}
4151      */
4152     getFieldValues : function(with_hidden)
4153     {
4154         var items = this.getItems();
4155         var ret = {};
4156         items.each(function(f){
4157             if (!f.getName()) {
4158                 return;
4159             }
4160             var v = f.getValue();
4161             if (f.inputType =='radio') {
4162                 if (typeof(ret[f.getName()]) == 'undefined') {
4163                     ret[f.getName()] = ''; // empty..
4164                 }
4165                 
4166                 if (!f.el.dom.checked) {
4167                     return;
4168                     
4169                 }
4170                 v = f.el.dom.value;
4171                 
4172             }
4173             
4174             // not sure if this supported any more..
4175             if ((typeof(v) == 'object') && f.getRawValue) {
4176                 v = f.getRawValue() ; // dates..
4177             }
4178             // combo boxes where name != hiddenName...
4179             if (f.name != f.getName()) {
4180                 ret[f.name] = f.getRawValue();
4181             }
4182             ret[f.getName()] = v;
4183         });
4184         
4185         return ret;
4186     },
4187
4188     /**
4189      * Clears all invalid messages in this form.
4190      * @return {BasicForm} this
4191      */
4192     clearInvalid : function(){
4193         var items = this.getItems();
4194         
4195         items.each(function(f){
4196            f.clearInvalid();
4197         });
4198         
4199         
4200         
4201         return this;
4202     },
4203
4204     /**
4205      * Resets this form.
4206      * @return {BasicForm} this
4207      */
4208     reset : function(){
4209         var items = this.getItems();
4210         items.each(function(f){
4211             f.reset();
4212         });
4213         
4214         Roo.each(this.childForms || [], function (f) {
4215             f.reset();
4216         });
4217        
4218         
4219         return this;
4220     },
4221     getItems : function()
4222     {
4223         var r=new Roo.util.MixedCollection(false, function(o){
4224             return o.id || (o.id = Roo.id());
4225         });
4226         var iter = function(el) {
4227             if (el.inputEl) {
4228                 r.add(el);
4229             }
4230             if (!el.items) {
4231                 return;
4232             }
4233             Roo.each(el.items,function(e) {
4234                 iter(e);
4235             });
4236             
4237             
4238         };
4239         iter(this);
4240         return r;
4241         
4242         
4243         
4244         
4245     }
4246     
4247 });
4248
4249  
4250 /*
4251  * Based on:
4252  * Ext JS Library 1.1.1
4253  * Copyright(c) 2006-2007, Ext JS, LLC.
4254  *
4255  * Originally Released Under LGPL - original licence link has changed is not relivant.
4256  *
4257  * Fork - LGPL
4258  * <script type="text/javascript">
4259  */
4260 /**
4261  * @class Roo.form.VTypes
4262  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4263  * @singleton
4264  */
4265 Roo.form.VTypes = function(){
4266     // closure these in so they are only created once.
4267     var alpha = /^[a-zA-Z_]+$/;
4268     var alphanum = /^[a-zA-Z0-9_]+$/;
4269     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4270     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4271
4272     // All these messages and functions are configurable
4273     return {
4274         /**
4275          * The function used to validate email addresses
4276          * @param {String} value The email address
4277          */
4278         'email' : function(v){
4279             return email.test(v);
4280         },
4281         /**
4282          * The error text to display when the email validation function returns false
4283          * @type String
4284          */
4285         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4286         /**
4287          * The keystroke filter mask to be applied on email input
4288          * @type RegExp
4289          */
4290         'emailMask' : /[a-z0-9_\.\-@]/i,
4291
4292         /**
4293          * The function used to validate URLs
4294          * @param {String} value The URL
4295          */
4296         'url' : function(v){
4297             return url.test(v);
4298         },
4299         /**
4300          * The error text to display when the url validation function returns false
4301          * @type String
4302          */
4303         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4304         
4305         /**
4306          * The function used to validate alpha values
4307          * @param {String} value The value
4308          */
4309         'alpha' : function(v){
4310             return alpha.test(v);
4311         },
4312         /**
4313          * The error text to display when the alpha validation function returns false
4314          * @type String
4315          */
4316         'alphaText' : 'This field should only contain letters and _',
4317         /**
4318          * The keystroke filter mask to be applied on alpha input
4319          * @type RegExp
4320          */
4321         'alphaMask' : /[a-z_]/i,
4322
4323         /**
4324          * The function used to validate alphanumeric values
4325          * @param {String} value The value
4326          */
4327         'alphanum' : function(v){
4328             return alphanum.test(v);
4329         },
4330         /**
4331          * The error text to display when the alphanumeric validation function returns false
4332          * @type String
4333          */
4334         'alphanumText' : 'This field should only contain letters, numbers and _',
4335         /**
4336          * The keystroke filter mask to be applied on alphanumeric input
4337          * @type RegExp
4338          */
4339         'alphanumMask' : /[a-z0-9_]/i
4340     };
4341 }();/*
4342  * - LGPL
4343  *
4344  * Input
4345  * 
4346  */
4347
4348 /**
4349  * @class Roo.bootstrap.Input
4350  * @extends Roo.bootstrap.Component
4351  * Bootstrap Input class
4352  * @cfg {Boolean} disabled is it disabled
4353  * @cfg {String} fieldLabel - the label associated
4354  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4355  * @cfg {String} name name of the input
4356  * @cfg {string} fieldLabel - the label associated
4357  * @cfg {string}  inputType - input / file submit ...
4358  * @cfg {string} placeholder - placeholder to put in text.
4359  * @cfg {string}  before - input group add on before
4360  * @cfg {string} after - input group add on after
4361  * @cfg {string} size - (lg|sm) or leave empty..
4362  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4363  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4364  * @cfg {Number} md colspan out of 12 for computer-sized screens
4365  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4366  * @cfg {string} value default value of the input
4367  * @cfg {Number} labelWidth set the width of label (0-12)
4368  * @cfg {String} labelAlign (top|left)
4369  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4370  * 
4371  * 
4372  * @constructor
4373  * Create a new Input
4374  * @param {Object} config The config object
4375  */
4376
4377 Roo.bootstrap.Input = function(config){
4378     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4379    
4380         this.addEvents({
4381             /**
4382              * @event focus
4383              * Fires when this field receives input focus.
4384              * @param {Roo.form.Field} this
4385              */
4386             focus : true,
4387             /**
4388              * @event blur
4389              * Fires when this field loses input focus.
4390              * @param {Roo.form.Field} this
4391              */
4392             blur : true,
4393             /**
4394              * @event specialkey
4395              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4396              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4397              * @param {Roo.form.Field} this
4398              * @param {Roo.EventObject} e The event object
4399              */
4400             specialkey : true,
4401             /**
4402              * @event change
4403              * Fires just before the field blurs if the field value has changed.
4404              * @param {Roo.form.Field} this
4405              * @param {Mixed} newValue The new value
4406              * @param {Mixed} oldValue The original value
4407              */
4408             change : true,
4409             /**
4410              * @event invalid
4411              * Fires after the field has been marked as invalid.
4412              * @param {Roo.form.Field} this
4413              * @param {String} msg The validation message
4414              */
4415             invalid : true,
4416             /**
4417              * @event valid
4418              * Fires after the field has been validated with no errors.
4419              * @param {Roo.form.Field} this
4420              */
4421             valid : true,
4422              /**
4423              * @event keyup
4424              * Fires after the key up
4425              * @param {Roo.form.Field} this
4426              * @param {Roo.EventObject}  e The event Object
4427              */
4428             keyup : true
4429         });
4430 };
4431
4432 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4433      /**
4434      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4435       automatic validation (defaults to "keyup").
4436      */
4437     validationEvent : "keyup",
4438      /**
4439      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4440      */
4441     validateOnBlur : true,
4442     /**
4443      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4444      */
4445     validationDelay : 250,
4446      /**
4447      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4448      */
4449     focusClass : "x-form-focus",  // not needed???
4450     
4451        
4452     /**
4453      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4454      */
4455     invalidClass : "has-error",
4456     
4457     /**
4458      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4459      */
4460     selectOnFocus : false,
4461     
4462      /**
4463      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4464      */
4465     maskRe : null,
4466        /**
4467      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4468      */
4469     vtype : null,
4470     
4471       /**
4472      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4473      */
4474     disableKeyFilter : false,
4475     
4476        /**
4477      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4478      */
4479     disabled : false,
4480      /**
4481      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4482      */
4483     allowBlank : true,
4484     /**
4485      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4486      */
4487     blankText : "This field is required",
4488     
4489      /**
4490      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4491      */
4492     minLength : 0,
4493     /**
4494      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4495      */
4496     maxLength : Number.MAX_VALUE,
4497     /**
4498      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4499      */
4500     minLengthText : "The minimum length for this field is {0}",
4501     /**
4502      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4503      */
4504     maxLengthText : "The maximum length for this field is {0}",
4505   
4506     
4507     /**
4508      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4509      * If available, this function will be called only after the basic validators all return true, and will be passed the
4510      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4511      */
4512     validator : null,
4513     /**
4514      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4515      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4516      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4517      */
4518     regex : null,
4519     /**
4520      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4521      */
4522     regexText : "",
4523     
4524     
4525     
4526     fieldLabel : '',
4527     inputType : 'text',
4528     
4529     name : false,
4530     placeholder: false,
4531     before : false,
4532     after : false,
4533     size : false,
4534     // private
4535     hasFocus : false,
4536     preventMark: false,
4537     isFormField : true,
4538     value : '',
4539     labelWidth : 2,
4540     labelAlign : false,
4541     readOnly : false,
4542     
4543     parentLabelAlign : function()
4544     {
4545         var parent = this;
4546         while (parent.parent()) {
4547             parent = parent.parent();
4548             if (typeof(parent.labelAlign) !='undefined') {
4549                 return parent.labelAlign;
4550             }
4551         }
4552         return 'left';
4553         
4554     },
4555     
4556     getAutoCreate : function(){
4557         
4558         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4559         
4560         var id = Roo.id();
4561         
4562         var cfg = {};
4563         
4564         if(this.inputType != 'hidden'){
4565             cfg.cls = 'form-group' //input-group
4566         }
4567         
4568         var input =  {
4569             tag: 'input',
4570             id : id,
4571             type : this.inputType,
4572             value : this.value,
4573             cls : 'form-control',
4574             placeholder : this.placeholder || ''
4575             
4576         };
4577         
4578         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4579             input.maxLength = this.maxLength;
4580         }
4581         
4582         if (this.disabled) {
4583             input.disabled=true;
4584         }
4585         
4586         if (this.readOnly) {
4587             input.readonly=true;
4588         }
4589         
4590         if (this.name) {
4591             input.name = this.name;
4592         }
4593         if (this.size) {
4594             input.cls += ' input-' + this.size;
4595         }
4596         var settings=this;
4597         ['xs','sm','md','lg'].map(function(size){
4598             if (settings[size]) {
4599                 cfg.cls += ' col-' + size + '-' + settings[size];
4600             }
4601         });
4602         
4603         var inputblock = input;
4604         
4605         if (this.before || this.after) {
4606             
4607             inputblock = {
4608                 cls : 'input-group',
4609                 cn :  [] 
4610             };
4611             if (this.before) {
4612                 inputblock.cn.push({
4613                     tag :'span',
4614                     cls : 'input-group-addon',
4615                     html : this.before
4616                 });
4617             }
4618             inputblock.cn.push(input);
4619             if (this.after) {
4620                 inputblock.cn.push({
4621                     tag :'span',
4622                     cls : 'input-group-addon',
4623                     html : this.after
4624                 });
4625             }
4626             
4627         };
4628         
4629         if (align ==='left' && this.fieldLabel.length) {
4630                 Roo.log("left and has label");
4631                 cfg.cn = [
4632                     
4633                     {
4634                         tag: 'label',
4635                         'for' :  id,
4636                         cls : 'control-label col-sm-' + this.labelWidth,
4637                         html : this.fieldLabel
4638                         
4639                     },
4640                     {
4641                         cls : "col-sm-" + (12 - this.labelWidth), 
4642                         cn: [
4643                             inputblock
4644                         ]
4645                     }
4646                     
4647                 ];
4648         } else if ( this.fieldLabel.length) {
4649                 Roo.log(" label");
4650                  cfg.cn = [
4651                    
4652                     {
4653                         tag: 'label',
4654                         //cls : 'input-group-addon',
4655                         html : this.fieldLabel
4656                         
4657                     },
4658                     
4659                     inputblock
4660                     
4661                 ];
4662
4663         } else {
4664             
4665                 Roo.log(" no label && no align");
4666                 cfg.cn = [
4667                     
4668                         inputblock
4669                     
4670                 ];
4671                 
4672                 
4673         };
4674         Roo.log('input-parentType: ' + this.parentType);
4675         
4676         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4677            cfg.cls += ' navbar-form';
4678            Roo.log(cfg);
4679         }
4680         
4681         return cfg;
4682         
4683     },
4684     /**
4685      * return the real input element.
4686      */
4687     inputEl: function ()
4688     {
4689         return this.el.select('input.form-control',true).first();
4690     },
4691     setDisabled : function(v)
4692     {
4693         var i  = this.inputEl().dom;
4694         if (!v) {
4695             i.removeAttribute('disabled');
4696             return;
4697             
4698         }
4699         i.setAttribute('disabled','true');
4700     },
4701     initEvents : function()
4702     {
4703         
4704         this.inputEl().on("keydown" , this.fireKey,  this);
4705         this.inputEl().on("focus", this.onFocus,  this);
4706         this.inputEl().on("blur", this.onBlur,  this);
4707         
4708         this.inputEl().relayEvent('keyup', this);
4709
4710         // reference to original value for reset
4711         this.originalValue = this.getValue();
4712         //Roo.form.TextField.superclass.initEvents.call(this);
4713         if(this.validationEvent == 'keyup'){
4714             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4715             this.inputEl().on('keyup', this.filterValidation, this);
4716         }
4717         else if(this.validationEvent !== false){
4718             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4719         }
4720         
4721         if(this.selectOnFocus){
4722             this.on("focus", this.preFocus, this);
4723             
4724         }
4725         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4726             this.inputEl().on("keypress", this.filterKeys, this);
4727         }
4728        /* if(this.grow){
4729             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4730             this.el.on("click", this.autoSize,  this);
4731         }
4732         */
4733         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4734             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4735         }
4736         
4737     },
4738     filterValidation : function(e){
4739         if(!e.isNavKeyPress()){
4740             this.validationTask.delay(this.validationDelay);
4741         }
4742     },
4743      /**
4744      * Validates the field value
4745      * @return {Boolean} True if the value is valid, else false
4746      */
4747     validate : function(){
4748         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4749         if(this.disabled || this.validateValue(this.getRawValue())){
4750             this.clearInvalid();
4751             return true;
4752         }
4753         return false;
4754     },
4755     
4756     
4757     /**
4758      * Validates a value according to the field's validation rules and marks the field as invalid
4759      * if the validation fails
4760      * @param {Mixed} value The value to validate
4761      * @return {Boolean} True if the value is valid, else false
4762      */
4763     validateValue : function(value){
4764         if(value.length < 1)  { // if it's blank
4765              if(this.allowBlank){
4766                 this.clearInvalid();
4767                 return true;
4768              }else{
4769                 this.markInvalid(this.blankText);
4770                 return false;
4771              }
4772         }
4773         if(value.length < this.minLength){
4774             this.markInvalid(String.format(this.minLengthText, this.minLength));
4775             return false;
4776         }
4777         if(value.length > this.maxLength){
4778             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4779             return false;
4780         }
4781         if(this.vtype){
4782             var vt = Roo.form.VTypes;
4783             if(!vt[this.vtype](value, this)){
4784                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4785                 return false;
4786             }
4787         }
4788         if(typeof this.validator == "function"){
4789             var msg = this.validator(value);
4790             if(msg !== true){
4791                 this.markInvalid(msg);
4792                 return false;
4793             }
4794         }
4795         if(this.regex && !this.regex.test(value)){
4796             this.markInvalid(this.regexText);
4797             return false;
4798         }
4799         return true;
4800     },
4801
4802     
4803     
4804      // private
4805     fireKey : function(e){
4806         //Roo.log('field ' + e.getKey());
4807         if(e.isNavKeyPress()){
4808             this.fireEvent("specialkey", this, e);
4809         }
4810     },
4811     focus : function (selectText){
4812         if(this.rendered){
4813             this.inputEl().focus();
4814             if(selectText === true){
4815                 this.inputEl().dom.select();
4816             }
4817         }
4818         return this;
4819     } ,
4820     
4821     onFocus : function(){
4822         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4823            // this.el.addClass(this.focusClass);
4824         }
4825         if(!this.hasFocus){
4826             this.hasFocus = true;
4827             this.startValue = this.getValue();
4828             this.fireEvent("focus", this);
4829         }
4830     },
4831     
4832     beforeBlur : Roo.emptyFn,
4833
4834     
4835     // private
4836     onBlur : function(){
4837         this.beforeBlur();
4838         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4839             //this.el.removeClass(this.focusClass);
4840         }
4841         this.hasFocus = false;
4842         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4843             this.validate();
4844         }
4845         var v = this.getValue();
4846         if(String(v) !== String(this.startValue)){
4847             this.fireEvent('change', this, v, this.startValue);
4848         }
4849         this.fireEvent("blur", this);
4850     },
4851     
4852     /**
4853      * Resets the current field value to the originally loaded value and clears any validation messages
4854      */
4855     reset : function(){
4856         this.setValue(this.originalValue);
4857         this.clearInvalid();
4858     },
4859      /**
4860      * Returns the name of the field
4861      * @return {Mixed} name The name field
4862      */
4863     getName: function(){
4864         return this.name;
4865     },
4866      /**
4867      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4868      * @return {Mixed} value The field value
4869      */
4870     getValue : function(){
4871         return this.inputEl().getValue();
4872     },
4873     /**
4874      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4875      * @return {Mixed} value The field value
4876      */
4877     getRawValue : function(){
4878         var v = this.inputEl().getValue();
4879         
4880         return v;
4881     },
4882     
4883     /**
4884      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4885      * @param {Mixed} value The value to set
4886      */
4887     setRawValue : function(v){
4888         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4889     },
4890     
4891     selectText : function(start, end){
4892         var v = this.getRawValue();
4893         if(v.length > 0){
4894             start = start === undefined ? 0 : start;
4895             end = end === undefined ? v.length : end;
4896             var d = this.inputEl().dom;
4897             if(d.setSelectionRange){
4898                 d.setSelectionRange(start, end);
4899             }else if(d.createTextRange){
4900                 var range = d.createTextRange();
4901                 range.moveStart("character", start);
4902                 range.moveEnd("character", v.length-end);
4903                 range.select();
4904             }
4905         }
4906     },
4907     
4908     /**
4909      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4910      * @param {Mixed} value The value to set
4911      */
4912     setValue : function(v){
4913         this.value = v;
4914         if(this.rendered){
4915             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4916             this.validate();
4917         }
4918     },
4919     
4920     /*
4921     processValue : function(value){
4922         if(this.stripCharsRe){
4923             var newValue = value.replace(this.stripCharsRe, '');
4924             if(newValue !== value){
4925                 this.setRawValue(newValue);
4926                 return newValue;
4927             }
4928         }
4929         return value;
4930     },
4931   */
4932     preFocus : function(){
4933         
4934         if(this.selectOnFocus){
4935             this.inputEl().dom.select();
4936         }
4937     },
4938     filterKeys : function(e){
4939         var k = e.getKey();
4940         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4941             return;
4942         }
4943         var c = e.getCharCode(), cc = String.fromCharCode(c);
4944         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4945             return;
4946         }
4947         if(!this.maskRe.test(cc)){
4948             e.stopEvent();
4949         }
4950     },
4951      /**
4952      * Clear any invalid styles/messages for this field
4953      */
4954     clearInvalid : function(){
4955         
4956         if(!this.el || this.preventMark){ // not rendered
4957             return;
4958         }
4959         this.el.removeClass(this.invalidClass);
4960         /*
4961         switch(this.msgTarget){
4962             case 'qtip':
4963                 this.el.dom.qtip = '';
4964                 break;
4965             case 'title':
4966                 this.el.dom.title = '';
4967                 break;
4968             case 'under':
4969                 if(this.errorEl){
4970                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4971                 }
4972                 break;
4973             case 'side':
4974                 if(this.errorIcon){
4975                     this.errorIcon.dom.qtip = '';
4976                     this.errorIcon.hide();
4977                     this.un('resize', this.alignErrorIcon, this);
4978                 }
4979                 break;
4980             default:
4981                 var t = Roo.getDom(this.msgTarget);
4982                 t.innerHTML = '';
4983                 t.style.display = 'none';
4984                 break;
4985         }
4986         */
4987         this.fireEvent('valid', this);
4988     },
4989      /**
4990      * Mark this field as invalid
4991      * @param {String} msg The validation message
4992      */
4993     markInvalid : function(msg){
4994         if(!this.el  || this.preventMark){ // not rendered
4995             return;
4996         }
4997         this.el.addClass(this.invalidClass);
4998         /*
4999         msg = msg || this.invalidText;
5000         switch(this.msgTarget){
5001             case 'qtip':
5002                 this.el.dom.qtip = msg;
5003                 this.el.dom.qclass = 'x-form-invalid-tip';
5004                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5005                     Roo.QuickTips.enable();
5006                 }
5007                 break;
5008             case 'title':
5009                 this.el.dom.title = msg;
5010                 break;
5011             case 'under':
5012                 if(!this.errorEl){
5013                     var elp = this.el.findParent('.x-form-element', 5, true);
5014                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5015                     this.errorEl.setWidth(elp.getWidth(true)-20);
5016                 }
5017                 this.errorEl.update(msg);
5018                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5019                 break;
5020             case 'side':
5021                 if(!this.errorIcon){
5022                     var elp = this.el.findParent('.x-form-element', 5, true);
5023                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5024                 }
5025                 this.alignErrorIcon();
5026                 this.errorIcon.dom.qtip = msg;
5027                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5028                 this.errorIcon.show();
5029                 this.on('resize', this.alignErrorIcon, this);
5030                 break;
5031             default:
5032                 var t = Roo.getDom(this.msgTarget);
5033                 t.innerHTML = msg;
5034                 t.style.display = this.msgDisplay;
5035                 break;
5036         }
5037         */
5038         this.fireEvent('invalid', this, msg);
5039     },
5040     // private
5041     SafariOnKeyDown : function(event)
5042     {
5043         // this is a workaround for a password hang bug on chrome/ webkit.
5044         
5045         var isSelectAll = false;
5046         
5047         if(this.inputEl().dom.selectionEnd > 0){
5048             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5049         }
5050         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5051             event.preventDefault();
5052             this.setValue('');
5053             return;
5054         }
5055         
5056         if(isSelectAll){ // backspace and delete key
5057             
5058             event.preventDefault();
5059             // this is very hacky as keydown always get's upper case.
5060             //
5061             var cc = String.fromCharCode(event.getCharCode());
5062             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5063             
5064         }
5065     },
5066     adjustWidth : function(tag, w){
5067         tag = tag.toLowerCase();
5068         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5069             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5070                 if(tag == 'input'){
5071                     return w + 2;
5072                 }
5073                 if(tag == 'textarea'){
5074                     return w-2;
5075                 }
5076             }else if(Roo.isOpera){
5077                 if(tag == 'input'){
5078                     return w + 2;
5079                 }
5080                 if(tag == 'textarea'){
5081                     return w-2;
5082                 }
5083             }
5084         }
5085         return w;
5086     }
5087     
5088 });
5089
5090  
5091 /*
5092  * - LGPL
5093  *
5094  * Input
5095  * 
5096  */
5097
5098 /**
5099  * @class Roo.bootstrap.TextArea
5100  * @extends Roo.bootstrap.Input
5101  * Bootstrap TextArea class
5102  * @cfg {Number} cols Specifies the visible width of a text area
5103  * @cfg {Number} rows Specifies the visible number of lines in a text area
5104  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5105  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5106  * @cfg {string} html text
5107  * 
5108  * @constructor
5109  * Create a new TextArea
5110  * @param {Object} config The config object
5111  */
5112
5113 Roo.bootstrap.TextArea = function(config){
5114     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5115    
5116 };
5117
5118 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5119      
5120     cols : false,
5121     rows : 5,
5122     readOnly : false,
5123     warp : 'soft',
5124     resize : false,
5125     value: false,
5126     html: false,
5127     
5128     getAutoCreate : function(){
5129         
5130         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5131         
5132         var id = Roo.id();
5133         
5134         var cfg = {};
5135         
5136         var input =  {
5137             tag: 'textarea',
5138             id : id,
5139             warp : this.warp,
5140             rows : this.rows,
5141             value : this.value || '',
5142             html: this.html || '',
5143             cls : 'form-control',
5144             placeholder : this.placeholder || '' 
5145             
5146         };
5147         
5148         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5149             input.maxLength = this.maxLength;
5150         }
5151         
5152         if(this.resize){
5153             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5154         }
5155         
5156         if(this.cols){
5157             input.cols = this.cols;
5158         }
5159         
5160         if (this.readOnly) {
5161             input.readonly = true;
5162         }
5163         
5164         if (this.name) {
5165             input.name = this.name;
5166         }
5167         
5168         if (this.size) {
5169             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5170         }
5171         
5172         var settings=this;
5173         ['xs','sm','md','lg'].map(function(size){
5174             if (settings[size]) {
5175                 cfg.cls += ' col-' + size + '-' + settings[size];
5176             }
5177         });
5178         
5179         var inputblock = input;
5180         
5181         if (this.before || this.after) {
5182             
5183             inputblock = {
5184                 cls : 'input-group',
5185                 cn :  [] 
5186             };
5187             if (this.before) {
5188                 inputblock.cn.push({
5189                     tag :'span',
5190                     cls : 'input-group-addon',
5191                     html : this.before
5192                 });
5193             }
5194             inputblock.cn.push(input);
5195             if (this.after) {
5196                 inputblock.cn.push({
5197                     tag :'span',
5198                     cls : 'input-group-addon',
5199                     html : this.after
5200                 });
5201             }
5202             
5203         }
5204         
5205         if (align ==='left' && this.fieldLabel.length) {
5206                 Roo.log("left and has label");
5207                 cfg.cn = [
5208                     
5209                     {
5210                         tag: 'label',
5211                         'for' :  id,
5212                         cls : 'control-label col-sm-' + this.labelWidth,
5213                         html : this.fieldLabel
5214                         
5215                     },
5216                     {
5217                         cls : "col-sm-" + (12 - this.labelWidth), 
5218                         cn: [
5219                             inputblock
5220                         ]
5221                     }
5222                     
5223                 ];
5224         } else if ( this.fieldLabel.length) {
5225                 Roo.log(" label");
5226                  cfg.cn = [
5227                    
5228                     {
5229                         tag: 'label',
5230                         //cls : 'input-group-addon',
5231                         html : this.fieldLabel
5232                         
5233                     },
5234                     
5235                     inputblock
5236                     
5237                 ];
5238
5239         } else {
5240             
5241                    Roo.log(" no label && no align");
5242                 cfg.cn = [
5243                     
5244                         inputblock
5245                     
5246                 ];
5247                 
5248                 
5249         }
5250         
5251         if (this.disabled) {
5252             input.disabled=true;
5253         }
5254         
5255         return cfg;
5256         
5257     },
5258     /**
5259      * return the real textarea element.
5260      */
5261     inputEl: function ()
5262     {
5263         return this.el.select('textarea.form-control',true).first();
5264     }
5265 });
5266
5267  
5268 /*
5269  * - LGPL
5270  *
5271  * trigger field - base class for combo..
5272  * 
5273  */
5274  
5275 /**
5276  * @class Roo.bootstrap.TriggerField
5277  * @extends Roo.bootstrap.Input
5278  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5279  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5280  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5281  * for which you can provide a custom implementation.  For example:
5282  * <pre><code>
5283 var trigger = new Roo.bootstrap.TriggerField();
5284 trigger.onTriggerClick = myTriggerFn;
5285 trigger.applyTo('my-field');
5286 </code></pre>
5287  *
5288  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5289  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5290  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5291  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5292  * @constructor
5293  * Create a new TriggerField.
5294  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5295  * to the base TextField)
5296  */
5297 Roo.bootstrap.TriggerField = function(config){
5298     this.mimicing = false;
5299     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5300 };
5301
5302 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5303     /**
5304      * @cfg {String} triggerClass A CSS class to apply to the trigger
5305      */
5306      /**
5307      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5308      */
5309     hideTrigger:false,
5310
5311     /** @cfg {Boolean} grow @hide */
5312     /** @cfg {Number} growMin @hide */
5313     /** @cfg {Number} growMax @hide */
5314
5315     /**
5316      * @hide 
5317      * @method
5318      */
5319     autoSize: Roo.emptyFn,
5320     // private
5321     monitorTab : true,
5322     // private
5323     deferHeight : true,
5324
5325     
5326     actionMode : 'wrap',
5327     
5328     
5329     
5330     getAutoCreate : function(){
5331        
5332         var parent = this.parent();
5333         
5334         var align = this.parentLabelAlign();
5335         
5336         var id = Roo.id();
5337         
5338         var cfg = {
5339             cls: 'form-group' //input-group
5340         };
5341         
5342         
5343         var input =  {
5344             tag: 'input',
5345             id : id,
5346             type : this.inputType,
5347             cls : 'form-control',
5348             autocomplete: 'off',
5349             placeholder : this.placeholder || '' 
5350             
5351         };
5352         if (this.name) {
5353             input.name = this.name;
5354         }
5355         if (this.size) {
5356             input.cls += ' input-' + this.size;
5357         }
5358         
5359         if (this.disabled) {
5360             input.disabled=true;
5361         }
5362         
5363         var inputblock = input;
5364         
5365         if (this.before || this.after) {
5366             
5367             inputblock = {
5368                 cls : 'input-group',
5369                 cn :  [] 
5370             };
5371             if (this.before) {
5372                 inputblock.cn.push({
5373                     tag :'span',
5374                     cls : 'input-group-addon',
5375                     html : this.before
5376                 });
5377             }
5378             inputblock.cn.push(input);
5379             if (this.after) {
5380                 inputblock.cn.push({
5381                     tag :'span',
5382                     cls : 'input-group-addon',
5383                     html : this.after
5384                 });
5385             }
5386             
5387         };
5388         
5389         var box = {
5390             tag: 'div',
5391             cn: [
5392                 {
5393                     tag: 'input',
5394                     type : 'hidden',
5395                     cls: 'form-hidden-field'
5396                 },
5397                 inputblock
5398             ]
5399             
5400         };
5401         
5402         if(this.multiple){
5403             Roo.log('multiple');
5404             
5405             box = {
5406                 tag: 'div',
5407                 cn: [
5408                     {
5409                         tag: 'input',
5410                         type : 'hidden',
5411                         cls: 'form-hidden-field'
5412                     },
5413                     {
5414                         tag: 'ul',
5415                         cls: 'select2-choices',
5416                         cn:[
5417                             {
5418                                 tag: 'li',
5419                                 cls: 'select2-search-field',
5420                                 cn: [
5421
5422                                     inputblock
5423                                 ]
5424                             }
5425                         ]
5426                     }
5427                 ]
5428             }
5429         };
5430         
5431         var combobox = {
5432             cls: 'select2-container input-group',
5433             cn: [
5434                 box,
5435                 {
5436                     tag: 'ul',
5437                     cls: 'typeahead typeahead-long dropdown-menu',
5438                     style: 'display:none'
5439                 }
5440             ]
5441         };
5442         
5443         if(!this.multiple){
5444             combobox.cn.push({
5445                 tag :'span',
5446                 cls : 'input-group-addon btn dropdown-toggle',
5447                 cn : [
5448                     {
5449                         tag: 'span',
5450                         cls: 'caret'
5451                     },
5452                     {
5453                         tag: 'span',
5454                         cls: 'combobox-clear',
5455                         cn  : [
5456                             {
5457                                 tag : 'i',
5458                                 cls: 'icon-remove'
5459                             }
5460                         ]
5461                     }
5462                 ]
5463
5464             })
5465         }
5466         
5467         if(this.multiple){
5468             combobox.cls += ' select2-container-multi';
5469         }
5470         
5471         if (align ==='left' && this.fieldLabel.length) {
5472             
5473                 Roo.log("left and has label");
5474                 cfg.cn = [
5475                     
5476                     {
5477                         tag: 'label',
5478                         'for' :  id,
5479                         cls : 'control-label col-sm-' + this.labelWidth,
5480                         html : this.fieldLabel
5481                         
5482                     },
5483                     {
5484                         cls : "col-sm-" + (12 - this.labelWidth), 
5485                         cn: [
5486                             combobox
5487                         ]
5488                     }
5489                     
5490                 ];
5491         } else if ( this.fieldLabel.length) {
5492                 Roo.log(" label");
5493                  cfg.cn = [
5494                    
5495                     {
5496                         tag: 'label',
5497                         //cls : 'input-group-addon',
5498                         html : this.fieldLabel
5499                         
5500                     },
5501                     
5502                     combobox
5503                     
5504                 ];
5505
5506         } else {
5507             
5508                 Roo.log(" no label && no align");
5509                 cfg = combobox
5510                      
5511                 
5512         }
5513          
5514         var settings=this;
5515         ['xs','sm','md','lg'].map(function(size){
5516             if (settings[size]) {
5517                 cfg.cls += ' col-' + size + '-' + settings[size];
5518             }
5519         });
5520         
5521         return cfg;
5522         
5523     },
5524     
5525     
5526     
5527     // private
5528     onResize : function(w, h){
5529 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5530 //        if(typeof w == 'number'){
5531 //            var x = w - this.trigger.getWidth();
5532 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5533 //            this.trigger.setStyle('left', x+'px');
5534 //        }
5535     },
5536
5537     // private
5538     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5539
5540     // private
5541     getResizeEl : function(){
5542         return this.inputEl();
5543     },
5544
5545     // private
5546     getPositionEl : function(){
5547         return this.inputEl();
5548     },
5549
5550     // private
5551     alignErrorIcon : function(){
5552         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5553     },
5554
5555     // private
5556     initEvents : function(){
5557         
5558         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5559         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5560         if(!this.multiple){
5561             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5562             if(this.hideTrigger){
5563                 this.trigger.setDisplayed(false);
5564             }
5565             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5566         }
5567         
5568         if(this.multiple){
5569             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5570         }
5571         
5572         //this.trigger.addClassOnOver('x-form-trigger-over');
5573         //this.trigger.addClassOnClick('x-form-trigger-click');
5574         
5575         //if(!this.width){
5576         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5577         //}
5578     },
5579
5580     // private
5581     initTrigger : function(){
5582        
5583     },
5584
5585     // private
5586     onDestroy : function(){
5587         if(this.trigger){
5588             this.trigger.removeAllListeners();
5589           //  this.trigger.remove();
5590         }
5591         //if(this.wrap){
5592         //    this.wrap.remove();
5593         //}
5594         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5595     },
5596
5597     // private
5598     onFocus : function(){
5599         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5600         /*
5601         if(!this.mimicing){
5602             this.wrap.addClass('x-trigger-wrap-focus');
5603             this.mimicing = true;
5604             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5605             if(this.monitorTab){
5606                 this.el.on("keydown", this.checkTab, this);
5607             }
5608         }
5609         */
5610     },
5611
5612     // private
5613     checkTab : function(e){
5614         if(e.getKey() == e.TAB){
5615             this.triggerBlur();
5616         }
5617     },
5618
5619     // private
5620     onBlur : function(){
5621         // do nothing
5622     },
5623
5624     // private
5625     mimicBlur : function(e, t){
5626         /*
5627         if(!this.wrap.contains(t) && this.validateBlur()){
5628             this.triggerBlur();
5629         }
5630         */
5631     },
5632
5633     // private
5634     triggerBlur : function(){
5635         this.mimicing = false;
5636         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5637         if(this.monitorTab){
5638             this.el.un("keydown", this.checkTab, this);
5639         }
5640         //this.wrap.removeClass('x-trigger-wrap-focus');
5641         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5642     },
5643
5644     // private
5645     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5646     validateBlur : function(e, t){
5647         return true;
5648     },
5649
5650     // private
5651     onDisable : function(){
5652         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5653         //if(this.wrap){
5654         //    this.wrap.addClass('x-item-disabled');
5655         //}
5656     },
5657
5658     // private
5659     onEnable : function(){
5660         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5661         //if(this.wrap){
5662         //    this.el.removeClass('x-item-disabled');
5663         //}
5664     },
5665
5666     // private
5667     onShow : function(){
5668         var ae = this.getActionEl();
5669         
5670         if(ae){
5671             ae.dom.style.display = '';
5672             ae.dom.style.visibility = 'visible';
5673         }
5674     },
5675
5676     // private
5677     
5678     onHide : function(){
5679         var ae = this.getActionEl();
5680         ae.dom.style.display = 'none';
5681     },
5682
5683     /**
5684      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5685      * by an implementing function.
5686      * @method
5687      * @param {EventObject} e
5688      */
5689     onTriggerClick : Roo.emptyFn
5690 });
5691  /*
5692  * Based on:
5693  * Ext JS Library 1.1.1
5694  * Copyright(c) 2006-2007, Ext JS, LLC.
5695  *
5696  * Originally Released Under LGPL - original licence link has changed is not relivant.
5697  *
5698  * Fork - LGPL
5699  * <script type="text/javascript">
5700  */
5701
5702
5703 /**
5704  * @class Roo.data.SortTypes
5705  * @singleton
5706  * Defines the default sorting (casting?) comparison functions used when sorting data.
5707  */
5708 Roo.data.SortTypes = {
5709     /**
5710      * Default sort that does nothing
5711      * @param {Mixed} s The value being converted
5712      * @return {Mixed} The comparison value
5713      */
5714     none : function(s){
5715         return s;
5716     },
5717     
5718     /**
5719      * The regular expression used to strip tags
5720      * @type {RegExp}
5721      * @property
5722      */
5723     stripTagsRE : /<\/?[^>]+>/gi,
5724     
5725     /**
5726      * Strips all HTML tags to sort on text only
5727      * @param {Mixed} s The value being converted
5728      * @return {String} The comparison value
5729      */
5730     asText : function(s){
5731         return String(s).replace(this.stripTagsRE, "");
5732     },
5733     
5734     /**
5735      * Strips all HTML tags to sort on text only - Case insensitive
5736      * @param {Mixed} s The value being converted
5737      * @return {String} The comparison value
5738      */
5739     asUCText : function(s){
5740         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5741     },
5742     
5743     /**
5744      * Case insensitive string
5745      * @param {Mixed} s The value being converted
5746      * @return {String} The comparison value
5747      */
5748     asUCString : function(s) {
5749         return String(s).toUpperCase();
5750     },
5751     
5752     /**
5753      * Date sorting
5754      * @param {Mixed} s The value being converted
5755      * @return {Number} The comparison value
5756      */
5757     asDate : function(s) {
5758         if(!s){
5759             return 0;
5760         }
5761         if(s instanceof Date){
5762             return s.getTime();
5763         }
5764         return Date.parse(String(s));
5765     },
5766     
5767     /**
5768      * Float sorting
5769      * @param {Mixed} s The value being converted
5770      * @return {Float} The comparison value
5771      */
5772     asFloat : function(s) {
5773         var val = parseFloat(String(s).replace(/,/g, ""));
5774         if(isNaN(val)) val = 0;
5775         return val;
5776     },
5777     
5778     /**
5779      * Integer sorting
5780      * @param {Mixed} s The value being converted
5781      * @return {Number} The comparison value
5782      */
5783     asInt : function(s) {
5784         var val = parseInt(String(s).replace(/,/g, ""));
5785         if(isNaN(val)) val = 0;
5786         return val;
5787     }
5788 };/*
5789  * Based on:
5790  * Ext JS Library 1.1.1
5791  * Copyright(c) 2006-2007, Ext JS, LLC.
5792  *
5793  * Originally Released Under LGPL - original licence link has changed is not relivant.
5794  *
5795  * Fork - LGPL
5796  * <script type="text/javascript">
5797  */
5798
5799 /**
5800 * @class Roo.data.Record
5801  * Instances of this class encapsulate both record <em>definition</em> information, and record
5802  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5803  * to access Records cached in an {@link Roo.data.Store} object.<br>
5804  * <p>
5805  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5806  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5807  * objects.<br>
5808  * <p>
5809  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5810  * @constructor
5811  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5812  * {@link #create}. The parameters are the same.
5813  * @param {Array} data An associative Array of data values keyed by the field name.
5814  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5815  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5816  * not specified an integer id is generated.
5817  */
5818 Roo.data.Record = function(data, id){
5819     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5820     this.data = data;
5821 };
5822
5823 /**
5824  * Generate a constructor for a specific record layout.
5825  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5826  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5827  * Each field definition object may contain the following properties: <ul>
5828  * <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,
5829  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5830  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5831  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5832  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5833  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5834  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5835  * this may be omitted.</p></li>
5836  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5837  * <ul><li>auto (Default, implies no conversion)</li>
5838  * <li>string</li>
5839  * <li>int</li>
5840  * <li>float</li>
5841  * <li>boolean</li>
5842  * <li>date</li></ul></p></li>
5843  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5844  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5845  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5846  * by the Reader into an object that will be stored in the Record. It is passed the
5847  * following parameters:<ul>
5848  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5849  * </ul></p></li>
5850  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5851  * </ul>
5852  * <br>usage:<br><pre><code>
5853 var TopicRecord = Roo.data.Record.create(
5854     {name: 'title', mapping: 'topic_title'},
5855     {name: 'author', mapping: 'username'},
5856     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5857     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5858     {name: 'lastPoster', mapping: 'user2'},
5859     {name: 'excerpt', mapping: 'post_text'}
5860 );
5861
5862 var myNewRecord = new TopicRecord({
5863     title: 'Do my job please',
5864     author: 'noobie',
5865     totalPosts: 1,
5866     lastPost: new Date(),
5867     lastPoster: 'Animal',
5868     excerpt: 'No way dude!'
5869 });
5870 myStore.add(myNewRecord);
5871 </code></pre>
5872  * @method create
5873  * @static
5874  */
5875 Roo.data.Record.create = function(o){
5876     var f = function(){
5877         f.superclass.constructor.apply(this, arguments);
5878     };
5879     Roo.extend(f, Roo.data.Record);
5880     var p = f.prototype;
5881     p.fields = new Roo.util.MixedCollection(false, function(field){
5882         return field.name;
5883     });
5884     for(var i = 0, len = o.length; i < len; i++){
5885         p.fields.add(new Roo.data.Field(o[i]));
5886     }
5887     f.getField = function(name){
5888         return p.fields.get(name);  
5889     };
5890     return f;
5891 };
5892
5893 Roo.data.Record.AUTO_ID = 1000;
5894 Roo.data.Record.EDIT = 'edit';
5895 Roo.data.Record.REJECT = 'reject';
5896 Roo.data.Record.COMMIT = 'commit';
5897
5898 Roo.data.Record.prototype = {
5899     /**
5900      * Readonly flag - true if this record has been modified.
5901      * @type Boolean
5902      */
5903     dirty : false,
5904     editing : false,
5905     error: null,
5906     modified: null,
5907
5908     // private
5909     join : function(store){
5910         this.store = store;
5911     },
5912
5913     /**
5914      * Set the named field to the specified value.
5915      * @param {String} name The name of the field to set.
5916      * @param {Object} value The value to set the field to.
5917      */
5918     set : function(name, value){
5919         if(this.data[name] == value){
5920             return;
5921         }
5922         this.dirty = true;
5923         if(!this.modified){
5924             this.modified = {};
5925         }
5926         if(typeof this.modified[name] == 'undefined'){
5927             this.modified[name] = this.data[name];
5928         }
5929         this.data[name] = value;
5930         if(!this.editing && this.store){
5931             this.store.afterEdit(this);
5932         }       
5933     },
5934
5935     /**
5936      * Get the value of the named field.
5937      * @param {String} name The name of the field to get the value of.
5938      * @return {Object} The value of the field.
5939      */
5940     get : function(name){
5941         return this.data[name]; 
5942     },
5943
5944     // private
5945     beginEdit : function(){
5946         this.editing = true;
5947         this.modified = {}; 
5948     },
5949
5950     // private
5951     cancelEdit : function(){
5952         this.editing = false;
5953         delete this.modified;
5954     },
5955
5956     // private
5957     endEdit : function(){
5958         this.editing = false;
5959         if(this.dirty && this.store){
5960             this.store.afterEdit(this);
5961         }
5962     },
5963
5964     /**
5965      * Usually called by the {@link Roo.data.Store} which owns the Record.
5966      * Rejects all changes made to the Record since either creation, or the last commit operation.
5967      * Modified fields are reverted to their original values.
5968      * <p>
5969      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5970      * of reject operations.
5971      */
5972     reject : function(){
5973         var m = this.modified;
5974         for(var n in m){
5975             if(typeof m[n] != "function"){
5976                 this.data[n] = m[n];
5977             }
5978         }
5979         this.dirty = false;
5980         delete this.modified;
5981         this.editing = false;
5982         if(this.store){
5983             this.store.afterReject(this);
5984         }
5985     },
5986
5987     /**
5988      * Usually called by the {@link Roo.data.Store} which owns the Record.
5989      * Commits all changes made to the Record since either creation, or the last commit operation.
5990      * <p>
5991      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5992      * of commit operations.
5993      */
5994     commit : function(){
5995         this.dirty = false;
5996         delete this.modified;
5997         this.editing = false;
5998         if(this.store){
5999             this.store.afterCommit(this);
6000         }
6001     },
6002
6003     // private
6004     hasError : function(){
6005         return this.error != null;
6006     },
6007
6008     // private
6009     clearError : function(){
6010         this.error = null;
6011     },
6012
6013     /**
6014      * Creates a copy of this record.
6015      * @param {String} id (optional) A new record id if you don't want to use this record's id
6016      * @return {Record}
6017      */
6018     copy : function(newId) {
6019         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6020     }
6021 };/*
6022  * Based on:
6023  * Ext JS Library 1.1.1
6024  * Copyright(c) 2006-2007, Ext JS, LLC.
6025  *
6026  * Originally Released Under LGPL - original licence link has changed is not relivant.
6027  *
6028  * Fork - LGPL
6029  * <script type="text/javascript">
6030  */
6031
6032
6033
6034 /**
6035  * @class Roo.data.Store
6036  * @extends Roo.util.Observable
6037  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6038  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6039  * <p>
6040  * 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
6041  * has no knowledge of the format of the data returned by the Proxy.<br>
6042  * <p>
6043  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6044  * instances from the data object. These records are cached and made available through accessor functions.
6045  * @constructor
6046  * Creates a new Store.
6047  * @param {Object} config A config object containing the objects needed for the Store to access data,
6048  * and read the data into Records.
6049  */
6050 Roo.data.Store = function(config){
6051     this.data = new Roo.util.MixedCollection(false);
6052     this.data.getKey = function(o){
6053         return o.id;
6054     };
6055     this.baseParams = {};
6056     // private
6057     this.paramNames = {
6058         "start" : "start",
6059         "limit" : "limit",
6060         "sort" : "sort",
6061         "dir" : "dir",
6062         "multisort" : "_multisort"
6063     };
6064
6065     if(config && config.data){
6066         this.inlineData = config.data;
6067         delete config.data;
6068     }
6069
6070     Roo.apply(this, config);
6071     
6072     if(this.reader){ // reader passed
6073         this.reader = Roo.factory(this.reader, Roo.data);
6074         this.reader.xmodule = this.xmodule || false;
6075         if(!this.recordType){
6076             this.recordType = this.reader.recordType;
6077         }
6078         if(this.reader.onMetaChange){
6079             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6080         }
6081     }
6082
6083     if(this.recordType){
6084         this.fields = this.recordType.prototype.fields;
6085     }
6086     this.modified = [];
6087
6088     this.addEvents({
6089         /**
6090          * @event datachanged
6091          * Fires when the data cache has changed, and a widget which is using this Store
6092          * as a Record cache should refresh its view.
6093          * @param {Store} this
6094          */
6095         datachanged : true,
6096         /**
6097          * @event metachange
6098          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6099          * @param {Store} this
6100          * @param {Object} meta The JSON metadata
6101          */
6102         metachange : true,
6103         /**
6104          * @event add
6105          * Fires when Records have been added to the Store
6106          * @param {Store} this
6107          * @param {Roo.data.Record[]} records The array of Records added
6108          * @param {Number} index The index at which the record(s) were added
6109          */
6110         add : true,
6111         /**
6112          * @event remove
6113          * Fires when a Record has been removed from the Store
6114          * @param {Store} this
6115          * @param {Roo.data.Record} record The Record that was removed
6116          * @param {Number} index The index at which the record was removed
6117          */
6118         remove : true,
6119         /**
6120          * @event update
6121          * Fires when a Record has been updated
6122          * @param {Store} this
6123          * @param {Roo.data.Record} record The Record that was updated
6124          * @param {String} operation The update operation being performed.  Value may be one of:
6125          * <pre><code>
6126  Roo.data.Record.EDIT
6127  Roo.data.Record.REJECT
6128  Roo.data.Record.COMMIT
6129          * </code></pre>
6130          */
6131         update : true,
6132         /**
6133          * @event clear
6134          * Fires when the data cache has been cleared.
6135          * @param {Store} this
6136          */
6137         clear : true,
6138         /**
6139          * @event beforeload
6140          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6141          * the load action will be canceled.
6142          * @param {Store} this
6143          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6144          */
6145         beforeload : true,
6146         /**
6147          * @event beforeloadadd
6148          * Fires after a new set of Records has been loaded.
6149          * @param {Store} this
6150          * @param {Roo.data.Record[]} records The Records that were loaded
6151          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6152          */
6153         beforeloadadd : true,
6154         /**
6155          * @event load
6156          * Fires after a new set of Records has been loaded, before they are added to the store.
6157          * @param {Store} this
6158          * @param {Roo.data.Record[]} records The Records that were loaded
6159          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6160          * @params {Object} return from reader
6161          */
6162         load : true,
6163         /**
6164          * @event loadexception
6165          * Fires if an exception occurs in the Proxy during loading.
6166          * Called with the signature of the Proxy's "loadexception" event.
6167          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6168          * 
6169          * @param {Proxy} 
6170          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6171          * @param {Object} load options 
6172          * @param {Object} jsonData from your request (normally this contains the Exception)
6173          */
6174         loadexception : true
6175     });
6176     
6177     if(this.proxy){
6178         this.proxy = Roo.factory(this.proxy, Roo.data);
6179         this.proxy.xmodule = this.xmodule || false;
6180         this.relayEvents(this.proxy,  ["loadexception"]);
6181     }
6182     this.sortToggle = {};
6183     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6184
6185     Roo.data.Store.superclass.constructor.call(this);
6186
6187     if(this.inlineData){
6188         this.loadData(this.inlineData);
6189         delete this.inlineData;
6190     }
6191 };
6192
6193 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6194      /**
6195     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6196     * without a remote query - used by combo/forms at present.
6197     */
6198     
6199     /**
6200     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6201     */
6202     /**
6203     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6204     */
6205     /**
6206     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6207     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6208     */
6209     /**
6210     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6211     * on any HTTP request
6212     */
6213     /**
6214     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6215     */
6216     /**
6217     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6218     */
6219     multiSort: false,
6220     /**
6221     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6222     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6223     */
6224     remoteSort : false,
6225
6226     /**
6227     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6228      * loaded or when a record is removed. (defaults to false).
6229     */
6230     pruneModifiedRecords : false,
6231
6232     // private
6233     lastOptions : null,
6234
6235     /**
6236      * Add Records to the Store and fires the add event.
6237      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6238      */
6239     add : function(records){
6240         records = [].concat(records);
6241         for(var i = 0, len = records.length; i < len; i++){
6242             records[i].join(this);
6243         }
6244         var index = this.data.length;
6245         this.data.addAll(records);
6246         this.fireEvent("add", this, records, index);
6247     },
6248
6249     /**
6250      * Remove a Record from the Store and fires the remove event.
6251      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6252      */
6253     remove : function(record){
6254         var index = this.data.indexOf(record);
6255         this.data.removeAt(index);
6256         if(this.pruneModifiedRecords){
6257             this.modified.remove(record);
6258         }
6259         this.fireEvent("remove", this, record, index);
6260     },
6261
6262     /**
6263      * Remove all Records from the Store and fires the clear event.
6264      */
6265     removeAll : function(){
6266         this.data.clear();
6267         if(this.pruneModifiedRecords){
6268             this.modified = [];
6269         }
6270         this.fireEvent("clear", this);
6271     },
6272
6273     /**
6274      * Inserts Records to the Store at the given index and fires the add event.
6275      * @param {Number} index The start index at which to insert the passed Records.
6276      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6277      */
6278     insert : function(index, records){
6279         records = [].concat(records);
6280         for(var i = 0, len = records.length; i < len; i++){
6281             this.data.insert(index, records[i]);
6282             records[i].join(this);
6283         }
6284         this.fireEvent("add", this, records, index);
6285     },
6286
6287     /**
6288      * Get the index within the cache of the passed Record.
6289      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6290      * @return {Number} The index of the passed Record. Returns -1 if not found.
6291      */
6292     indexOf : function(record){
6293         return this.data.indexOf(record);
6294     },
6295
6296     /**
6297      * Get the index within the cache of the Record with the passed id.
6298      * @param {String} id The id of the Record to find.
6299      * @return {Number} The index of the Record. Returns -1 if not found.
6300      */
6301     indexOfId : function(id){
6302         return this.data.indexOfKey(id);
6303     },
6304
6305     /**
6306      * Get the Record with the specified id.
6307      * @param {String} id The id of the Record to find.
6308      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6309      */
6310     getById : function(id){
6311         return this.data.key(id);
6312     },
6313
6314     /**
6315      * Get the Record at the specified index.
6316      * @param {Number} index The index of the Record to find.
6317      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6318      */
6319     getAt : function(index){
6320         return this.data.itemAt(index);
6321     },
6322
6323     /**
6324      * Returns a range of Records between specified indices.
6325      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6326      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6327      * @return {Roo.data.Record[]} An array of Records
6328      */
6329     getRange : function(start, end){
6330         return this.data.getRange(start, end);
6331     },
6332
6333     // private
6334     storeOptions : function(o){
6335         o = Roo.apply({}, o);
6336         delete o.callback;
6337         delete o.scope;
6338         this.lastOptions = o;
6339     },
6340
6341     /**
6342      * Loads the Record cache from the configured Proxy using the configured Reader.
6343      * <p>
6344      * If using remote paging, then the first load call must specify the <em>start</em>
6345      * and <em>limit</em> properties in the options.params property to establish the initial
6346      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6347      * <p>
6348      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6349      * and this call will return before the new data has been loaded. Perform any post-processing
6350      * in a callback function, or in a "load" event handler.</strong>
6351      * <p>
6352      * @param {Object} options An object containing properties which control loading options:<ul>
6353      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6354      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6355      * passed the following arguments:<ul>
6356      * <li>r : Roo.data.Record[]</li>
6357      * <li>options: Options object from the load call</li>
6358      * <li>success: Boolean success indicator</li></ul></li>
6359      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6360      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6361      * </ul>
6362      */
6363     load : function(options){
6364         options = options || {};
6365         if(this.fireEvent("beforeload", this, options) !== false){
6366             this.storeOptions(options);
6367             var p = Roo.apply(options.params || {}, this.baseParams);
6368             // if meta was not loaded from remote source.. try requesting it.
6369             if (!this.reader.metaFromRemote) {
6370                 p._requestMeta = 1;
6371             }
6372             if(this.sortInfo && this.remoteSort){
6373                 var pn = this.paramNames;
6374                 p[pn["sort"]] = this.sortInfo.field;
6375                 p[pn["dir"]] = this.sortInfo.direction;
6376             }
6377             if (this.multiSort) {
6378                 var pn = this.paramNames;
6379                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6380             }
6381             
6382             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6383         }
6384     },
6385
6386     /**
6387      * Reloads the Record cache from the configured Proxy using the configured Reader and
6388      * the options from the last load operation performed.
6389      * @param {Object} options (optional) An object containing properties which may override the options
6390      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6391      * the most recently used options are reused).
6392      */
6393     reload : function(options){
6394         this.load(Roo.applyIf(options||{}, this.lastOptions));
6395     },
6396
6397     // private
6398     // Called as a callback by the Reader during a load operation.
6399     loadRecords : function(o, options, success){
6400         if(!o || success === false){
6401             if(success !== false){
6402                 this.fireEvent("load", this, [], options, o);
6403             }
6404             if(options.callback){
6405                 options.callback.call(options.scope || this, [], options, false);
6406             }
6407             return;
6408         }
6409         // if data returned failure - throw an exception.
6410         if (o.success === false) {
6411             // show a message if no listener is registered.
6412             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6413                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6414             }
6415             // loadmask wil be hooked into this..
6416             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6417             return;
6418         }
6419         var r = o.records, t = o.totalRecords || r.length;
6420         
6421         this.fireEvent("beforeloadadd", this, r, options, o);
6422         
6423         if(!options || options.add !== true){
6424             if(this.pruneModifiedRecords){
6425                 this.modified = [];
6426             }
6427             for(var i = 0, len = r.length; i < len; i++){
6428                 r[i].join(this);
6429             }
6430             if(this.snapshot){
6431                 this.data = this.snapshot;
6432                 delete this.snapshot;
6433             }
6434             this.data.clear();
6435             this.data.addAll(r);
6436             this.totalLength = t;
6437             this.applySort();
6438             this.fireEvent("datachanged", this);
6439         }else{
6440             this.totalLength = Math.max(t, this.data.length+r.length);
6441             this.add(r);
6442         }
6443         this.fireEvent("load", this, r, options, o);
6444         if(options.callback){
6445             options.callback.call(options.scope || this, r, options, true);
6446         }
6447     },
6448
6449
6450     /**
6451      * Loads data from a passed data block. A Reader which understands the format of the data
6452      * must have been configured in the constructor.
6453      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6454      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6455      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6456      */
6457     loadData : function(o, append){
6458         var r = this.reader.readRecords(o);
6459         this.loadRecords(r, {add: append}, true);
6460     },
6461
6462     /**
6463      * Gets the number of cached records.
6464      * <p>
6465      * <em>If using paging, this may not be the total size of the dataset. If the data object
6466      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6467      * the data set size</em>
6468      */
6469     getCount : function(){
6470         return this.data.length || 0;
6471     },
6472
6473     /**
6474      * Gets the total number of records in the dataset as returned by the server.
6475      * <p>
6476      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6477      * the dataset size</em>
6478      */
6479     getTotalCount : function(){
6480         return this.totalLength || 0;
6481     },
6482
6483     /**
6484      * Returns the sort state of the Store as an object with two properties:
6485      * <pre><code>
6486  field {String} The name of the field by which the Records are sorted
6487  direction {String} The sort order, "ASC" or "DESC"
6488      * </code></pre>
6489      */
6490     getSortState : function(){
6491         return this.sortInfo;
6492     },
6493
6494     // private
6495     applySort : function(){
6496         if(this.sortInfo && !this.remoteSort){
6497             var s = this.sortInfo, f = s.field;
6498             var st = this.fields.get(f).sortType;
6499             var fn = function(r1, r2){
6500                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6501                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6502             };
6503             this.data.sort(s.direction, fn);
6504             if(this.snapshot && this.snapshot != this.data){
6505                 this.snapshot.sort(s.direction, fn);
6506             }
6507         }
6508     },
6509
6510     /**
6511      * Sets the default sort column and order to be used by the next load operation.
6512      * @param {String} fieldName The name of the field to sort by.
6513      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6514      */
6515     setDefaultSort : function(field, dir){
6516         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6517     },
6518
6519     /**
6520      * Sort the Records.
6521      * If remote sorting is used, the sort is performed on the server, and the cache is
6522      * reloaded. If local sorting is used, the cache is sorted internally.
6523      * @param {String} fieldName The name of the field to sort by.
6524      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6525      */
6526     sort : function(fieldName, dir){
6527         var f = this.fields.get(fieldName);
6528         if(!dir){
6529             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6530             
6531             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6532                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6533             }else{
6534                 dir = f.sortDir;
6535             }
6536         }
6537         this.sortToggle[f.name] = dir;
6538         this.sortInfo = {field: f.name, direction: dir};
6539         if(!this.remoteSort){
6540             this.applySort();
6541             this.fireEvent("datachanged", this);
6542         }else{
6543             this.load(this.lastOptions);
6544         }
6545     },
6546
6547     /**
6548      * Calls the specified function for each of the Records in the cache.
6549      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6550      * Returning <em>false</em> aborts and exits the iteration.
6551      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6552      */
6553     each : function(fn, scope){
6554         this.data.each(fn, scope);
6555     },
6556
6557     /**
6558      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6559      * (e.g., during paging).
6560      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6561      */
6562     getModifiedRecords : function(){
6563         return this.modified;
6564     },
6565
6566     // private
6567     createFilterFn : function(property, value, anyMatch){
6568         if(!value.exec){ // not a regex
6569             value = String(value);
6570             if(value.length == 0){
6571                 return false;
6572             }
6573             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6574         }
6575         return function(r){
6576             return value.test(r.data[property]);
6577         };
6578     },
6579
6580     /**
6581      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6582      * @param {String} property A field on your records
6583      * @param {Number} start The record index to start at (defaults to 0)
6584      * @param {Number} end The last record index to include (defaults to length - 1)
6585      * @return {Number} The sum
6586      */
6587     sum : function(property, start, end){
6588         var rs = this.data.items, v = 0;
6589         start = start || 0;
6590         end = (end || end === 0) ? end : rs.length-1;
6591
6592         for(var i = start; i <= end; i++){
6593             v += (rs[i].data[property] || 0);
6594         }
6595         return v;
6596     },
6597
6598     /**
6599      * Filter the records by a specified property.
6600      * @param {String} field A field on your records
6601      * @param {String/RegExp} value Either a string that the field
6602      * should start with or a RegExp to test against the field
6603      * @param {Boolean} anyMatch True to match any part not just the beginning
6604      */
6605     filter : function(property, value, anyMatch){
6606         var fn = this.createFilterFn(property, value, anyMatch);
6607         return fn ? this.filterBy(fn) : this.clearFilter();
6608     },
6609
6610     /**
6611      * Filter by a function. The specified function will be called with each
6612      * record in this data source. If the function returns true the record is included,
6613      * otherwise it is filtered.
6614      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6615      * @param {Object} scope (optional) The scope of the function (defaults to this)
6616      */
6617     filterBy : function(fn, scope){
6618         this.snapshot = this.snapshot || this.data;
6619         this.data = this.queryBy(fn, scope||this);
6620         this.fireEvent("datachanged", this);
6621     },
6622
6623     /**
6624      * Query the records by a specified property.
6625      * @param {String} field A field on your records
6626      * @param {String/RegExp} value Either a string that the field
6627      * should start with or a RegExp to test against the field
6628      * @param {Boolean} anyMatch True to match any part not just the beginning
6629      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6630      */
6631     query : function(property, value, anyMatch){
6632         var fn = this.createFilterFn(property, value, anyMatch);
6633         return fn ? this.queryBy(fn) : this.data.clone();
6634     },
6635
6636     /**
6637      * Query by a function. The specified function will be called with each
6638      * record in this data source. If the function returns true the record is included
6639      * in the results.
6640      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6641      * @param {Object} scope (optional) The scope of the function (defaults to this)
6642       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6643      **/
6644     queryBy : function(fn, scope){
6645         var data = this.snapshot || this.data;
6646         return data.filterBy(fn, scope||this);
6647     },
6648
6649     /**
6650      * Collects unique values for a particular dataIndex from this store.
6651      * @param {String} dataIndex The property to collect
6652      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6653      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6654      * @return {Array} An array of the unique values
6655      **/
6656     collect : function(dataIndex, allowNull, bypassFilter){
6657         var d = (bypassFilter === true && this.snapshot) ?
6658                 this.snapshot.items : this.data.items;
6659         var v, sv, r = [], l = {};
6660         for(var i = 0, len = d.length; i < len; i++){
6661             v = d[i].data[dataIndex];
6662             sv = String(v);
6663             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6664                 l[sv] = true;
6665                 r[r.length] = v;
6666             }
6667         }
6668         return r;
6669     },
6670
6671     /**
6672      * Revert to a view of the Record cache with no filtering applied.
6673      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6674      */
6675     clearFilter : function(suppressEvent){
6676         if(this.snapshot && this.snapshot != this.data){
6677             this.data = this.snapshot;
6678             delete this.snapshot;
6679             if(suppressEvent !== true){
6680                 this.fireEvent("datachanged", this);
6681             }
6682         }
6683     },
6684
6685     // private
6686     afterEdit : function(record){
6687         if(this.modified.indexOf(record) == -1){
6688             this.modified.push(record);
6689         }
6690         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6691     },
6692     
6693     // private
6694     afterReject : function(record){
6695         this.modified.remove(record);
6696         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6697     },
6698
6699     // private
6700     afterCommit : function(record){
6701         this.modified.remove(record);
6702         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6703     },
6704
6705     /**
6706      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6707      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6708      */
6709     commitChanges : function(){
6710         var m = this.modified.slice(0);
6711         this.modified = [];
6712         for(var i = 0, len = m.length; i < len; i++){
6713             m[i].commit();
6714         }
6715     },
6716
6717     /**
6718      * Cancel outstanding changes on all changed records.
6719      */
6720     rejectChanges : function(){
6721         var m = this.modified.slice(0);
6722         this.modified = [];
6723         for(var i = 0, len = m.length; i < len; i++){
6724             m[i].reject();
6725         }
6726     },
6727
6728     onMetaChange : function(meta, rtype, o){
6729         this.recordType = rtype;
6730         this.fields = rtype.prototype.fields;
6731         delete this.snapshot;
6732         this.sortInfo = meta.sortInfo || this.sortInfo;
6733         this.modified = [];
6734         this.fireEvent('metachange', this, this.reader.meta);
6735     },
6736     
6737     moveIndex : function(data, type)
6738     {
6739         var index = this.indexOf(data);
6740         
6741         var newIndex = index + type;
6742         
6743         this.remove(data);
6744         
6745         this.insert(newIndex, data);
6746         
6747     }
6748 });/*
6749  * Based on:
6750  * Ext JS Library 1.1.1
6751  * Copyright(c) 2006-2007, Ext JS, LLC.
6752  *
6753  * Originally Released Under LGPL - original licence link has changed is not relivant.
6754  *
6755  * Fork - LGPL
6756  * <script type="text/javascript">
6757  */
6758
6759 /**
6760  * @class Roo.data.SimpleStore
6761  * @extends Roo.data.Store
6762  * Small helper class to make creating Stores from Array data easier.
6763  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6764  * @cfg {Array} fields An array of field definition objects, or field name strings.
6765  * @cfg {Array} data The multi-dimensional array of data
6766  * @constructor
6767  * @param {Object} config
6768  */
6769 Roo.data.SimpleStore = function(config){
6770     Roo.data.SimpleStore.superclass.constructor.call(this, {
6771         isLocal : true,
6772         reader: new Roo.data.ArrayReader({
6773                 id: config.id
6774             },
6775             Roo.data.Record.create(config.fields)
6776         ),
6777         proxy : new Roo.data.MemoryProxy(config.data)
6778     });
6779     this.load();
6780 };
6781 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6782  * Based on:
6783  * Ext JS Library 1.1.1
6784  * Copyright(c) 2006-2007, Ext JS, LLC.
6785  *
6786  * Originally Released Under LGPL - original licence link has changed is not relivant.
6787  *
6788  * Fork - LGPL
6789  * <script type="text/javascript">
6790  */
6791
6792 /**
6793 /**
6794  * @extends Roo.data.Store
6795  * @class Roo.data.JsonStore
6796  * Small helper class to make creating Stores for JSON data easier. <br/>
6797 <pre><code>
6798 var store = new Roo.data.JsonStore({
6799     url: 'get-images.php',
6800     root: 'images',
6801     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6802 });
6803 </code></pre>
6804  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6805  * JsonReader and HttpProxy (unless inline data is provided).</b>
6806  * @cfg {Array} fields An array of field definition objects, or field name strings.
6807  * @constructor
6808  * @param {Object} config
6809  */
6810 Roo.data.JsonStore = function(c){
6811     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6812         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6813         reader: new Roo.data.JsonReader(c, c.fields)
6814     }));
6815 };
6816 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6817  * Based on:
6818  * Ext JS Library 1.1.1
6819  * Copyright(c) 2006-2007, Ext JS, LLC.
6820  *
6821  * Originally Released Under LGPL - original licence link has changed is not relivant.
6822  *
6823  * Fork - LGPL
6824  * <script type="text/javascript">
6825  */
6826
6827  
6828 Roo.data.Field = function(config){
6829     if(typeof config == "string"){
6830         config = {name: config};
6831     }
6832     Roo.apply(this, config);
6833     
6834     if(!this.type){
6835         this.type = "auto";
6836     }
6837     
6838     var st = Roo.data.SortTypes;
6839     // named sortTypes are supported, here we look them up
6840     if(typeof this.sortType == "string"){
6841         this.sortType = st[this.sortType];
6842     }
6843     
6844     // set default sortType for strings and dates
6845     if(!this.sortType){
6846         switch(this.type){
6847             case "string":
6848                 this.sortType = st.asUCString;
6849                 break;
6850             case "date":
6851                 this.sortType = st.asDate;
6852                 break;
6853             default:
6854                 this.sortType = st.none;
6855         }
6856     }
6857
6858     // define once
6859     var stripRe = /[\$,%]/g;
6860
6861     // prebuilt conversion function for this field, instead of
6862     // switching every time we're reading a value
6863     if(!this.convert){
6864         var cv, dateFormat = this.dateFormat;
6865         switch(this.type){
6866             case "":
6867             case "auto":
6868             case undefined:
6869                 cv = function(v){ return v; };
6870                 break;
6871             case "string":
6872                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6873                 break;
6874             case "int":
6875                 cv = function(v){
6876                     return v !== undefined && v !== null && v !== '' ?
6877                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6878                     };
6879                 break;
6880             case "float":
6881                 cv = function(v){
6882                     return v !== undefined && v !== null && v !== '' ?
6883                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6884                     };
6885                 break;
6886             case "bool":
6887             case "boolean":
6888                 cv = function(v){ return v === true || v === "true" || v == 1; };
6889                 break;
6890             case "date":
6891                 cv = function(v){
6892                     if(!v){
6893                         return '';
6894                     }
6895                     if(v instanceof Date){
6896                         return v;
6897                     }
6898                     if(dateFormat){
6899                         if(dateFormat == "timestamp"){
6900                             return new Date(v*1000);
6901                         }
6902                         return Date.parseDate(v, dateFormat);
6903                     }
6904                     var parsed = Date.parse(v);
6905                     return parsed ? new Date(parsed) : null;
6906                 };
6907              break;
6908             
6909         }
6910         this.convert = cv;
6911     }
6912 };
6913
6914 Roo.data.Field.prototype = {
6915     dateFormat: null,
6916     defaultValue: "",
6917     mapping: null,
6918     sortType : null,
6919     sortDir : "ASC"
6920 };/*
6921  * Based on:
6922  * Ext JS Library 1.1.1
6923  * Copyright(c) 2006-2007, Ext JS, LLC.
6924  *
6925  * Originally Released Under LGPL - original licence link has changed is not relivant.
6926  *
6927  * Fork - LGPL
6928  * <script type="text/javascript">
6929  */
6930  
6931 // Base class for reading structured data from a data source.  This class is intended to be
6932 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6933
6934 /**
6935  * @class Roo.data.DataReader
6936  * Base class for reading structured data from a data source.  This class is intended to be
6937  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6938  */
6939
6940 Roo.data.DataReader = function(meta, recordType){
6941     
6942     this.meta = meta;
6943     
6944     this.recordType = recordType instanceof Array ? 
6945         Roo.data.Record.create(recordType) : recordType;
6946 };
6947
6948 Roo.data.DataReader.prototype = {
6949      /**
6950      * Create an empty record
6951      * @param {Object} data (optional) - overlay some values
6952      * @return {Roo.data.Record} record created.
6953      */
6954     newRow :  function(d) {
6955         var da =  {};
6956         this.recordType.prototype.fields.each(function(c) {
6957             switch( c.type) {
6958                 case 'int' : da[c.name] = 0; break;
6959                 case 'date' : da[c.name] = new Date(); break;
6960                 case 'float' : da[c.name] = 0.0; break;
6961                 case 'boolean' : da[c.name] = false; break;
6962                 default : da[c.name] = ""; break;
6963             }
6964             
6965         });
6966         return new this.recordType(Roo.apply(da, d));
6967     }
6968     
6969 };/*
6970  * Based on:
6971  * Ext JS Library 1.1.1
6972  * Copyright(c) 2006-2007, Ext JS, LLC.
6973  *
6974  * Originally Released Under LGPL - original licence link has changed is not relivant.
6975  *
6976  * Fork - LGPL
6977  * <script type="text/javascript">
6978  */
6979
6980 /**
6981  * @class Roo.data.DataProxy
6982  * @extends Roo.data.Observable
6983  * This class is an abstract base class for implementations which provide retrieval of
6984  * unformatted data objects.<br>
6985  * <p>
6986  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6987  * (of the appropriate type which knows how to parse the data object) to provide a block of
6988  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6989  * <p>
6990  * Custom implementations must implement the load method as described in
6991  * {@link Roo.data.HttpProxy#load}.
6992  */
6993 Roo.data.DataProxy = function(){
6994     this.addEvents({
6995         /**
6996          * @event beforeload
6997          * Fires before a network request is made to retrieve a data object.
6998          * @param {Object} This DataProxy object.
6999          * @param {Object} params The params parameter to the load function.
7000          */
7001         beforeload : true,
7002         /**
7003          * @event load
7004          * Fires before the load method's callback is called.
7005          * @param {Object} This DataProxy object.
7006          * @param {Object} o The data object.
7007          * @param {Object} arg The callback argument object passed to the load function.
7008          */
7009         load : true,
7010         /**
7011          * @event loadexception
7012          * Fires if an Exception occurs during data retrieval.
7013          * @param {Object} This DataProxy object.
7014          * @param {Object} o The data object.
7015          * @param {Object} arg The callback argument object passed to the load function.
7016          * @param {Object} e The Exception.
7017          */
7018         loadexception : true
7019     });
7020     Roo.data.DataProxy.superclass.constructor.call(this);
7021 };
7022
7023 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7024
7025     /**
7026      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7027      */
7028 /*
7029  * Based on:
7030  * Ext JS Library 1.1.1
7031  * Copyright(c) 2006-2007, Ext JS, LLC.
7032  *
7033  * Originally Released Under LGPL - original licence link has changed is not relivant.
7034  *
7035  * Fork - LGPL
7036  * <script type="text/javascript">
7037  */
7038 /**
7039  * @class Roo.data.MemoryProxy
7040  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7041  * to the Reader when its load method is called.
7042  * @constructor
7043  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7044  */
7045 Roo.data.MemoryProxy = function(data){
7046     if (data.data) {
7047         data = data.data;
7048     }
7049     Roo.data.MemoryProxy.superclass.constructor.call(this);
7050     this.data = data;
7051 };
7052
7053 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7054     /**
7055      * Load data from the requested source (in this case an in-memory
7056      * data object passed to the constructor), read the data object into
7057      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7058      * process that block using the passed callback.
7059      * @param {Object} params This parameter is not used by the MemoryProxy class.
7060      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7061      * object into a block of Roo.data.Records.
7062      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7063      * The function must be passed <ul>
7064      * <li>The Record block object</li>
7065      * <li>The "arg" argument from the load function</li>
7066      * <li>A boolean success indicator</li>
7067      * </ul>
7068      * @param {Object} scope The scope in which to call the callback
7069      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7070      */
7071     load : function(params, reader, callback, scope, arg){
7072         params = params || {};
7073         var result;
7074         try {
7075             result = reader.readRecords(this.data);
7076         }catch(e){
7077             this.fireEvent("loadexception", this, arg, null, e);
7078             callback.call(scope, null, arg, false);
7079             return;
7080         }
7081         callback.call(scope, result, arg, true);
7082     },
7083     
7084     // private
7085     update : function(params, records){
7086         
7087     }
7088 });/*
7089  * Based on:
7090  * Ext JS Library 1.1.1
7091  * Copyright(c) 2006-2007, Ext JS, LLC.
7092  *
7093  * Originally Released Under LGPL - original licence link has changed is not relivant.
7094  *
7095  * Fork - LGPL
7096  * <script type="text/javascript">
7097  */
7098 /**
7099  * @class Roo.data.HttpProxy
7100  * @extends Roo.data.DataProxy
7101  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7102  * configured to reference a certain URL.<br><br>
7103  * <p>
7104  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7105  * from which the running page was served.<br><br>
7106  * <p>
7107  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7108  * <p>
7109  * Be aware that to enable the browser to parse an XML document, the server must set
7110  * the Content-Type header in the HTTP response to "text/xml".
7111  * @constructor
7112  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7113  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7114  * will be used to make the request.
7115  */
7116 Roo.data.HttpProxy = function(conn){
7117     Roo.data.HttpProxy.superclass.constructor.call(this);
7118     // is conn a conn config or a real conn?
7119     this.conn = conn;
7120     this.useAjax = !conn || !conn.events;
7121   
7122 };
7123
7124 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7125     // thse are take from connection...
7126     
7127     /**
7128      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7129      */
7130     /**
7131      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7132      * extra parameters to each request made by this object. (defaults to undefined)
7133      */
7134     /**
7135      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7136      *  to each request made by this object. (defaults to undefined)
7137      */
7138     /**
7139      * @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)
7140      */
7141     /**
7142      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7143      */
7144      /**
7145      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7146      * @type Boolean
7147      */
7148   
7149
7150     /**
7151      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7152      * @type Boolean
7153      */
7154     /**
7155      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7156      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7157      * a finer-grained basis than the DataProxy events.
7158      */
7159     getConnection : function(){
7160         return this.useAjax ? Roo.Ajax : this.conn;
7161     },
7162
7163     /**
7164      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7165      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7166      * process that block using the passed callback.
7167      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7168      * for the request to the remote server.
7169      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7170      * object into a block of Roo.data.Records.
7171      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7172      * The function must be passed <ul>
7173      * <li>The Record block object</li>
7174      * <li>The "arg" argument from the load function</li>
7175      * <li>A boolean success indicator</li>
7176      * </ul>
7177      * @param {Object} scope The scope in which to call the callback
7178      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7179      */
7180     load : function(params, reader, callback, scope, arg){
7181         if(this.fireEvent("beforeload", this, params) !== false){
7182             var  o = {
7183                 params : params || {},
7184                 request: {
7185                     callback : callback,
7186                     scope : scope,
7187                     arg : arg
7188                 },
7189                 reader: reader,
7190                 callback : this.loadResponse,
7191                 scope: this
7192             };
7193             if(this.useAjax){
7194                 Roo.applyIf(o, this.conn);
7195                 if(this.activeRequest){
7196                     Roo.Ajax.abort(this.activeRequest);
7197                 }
7198                 this.activeRequest = Roo.Ajax.request(o);
7199             }else{
7200                 this.conn.request(o);
7201             }
7202         }else{
7203             callback.call(scope||this, null, arg, false);
7204         }
7205     },
7206
7207     // private
7208     loadResponse : function(o, success, response){
7209         delete this.activeRequest;
7210         if(!success){
7211             this.fireEvent("loadexception", this, o, response);
7212             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7213             return;
7214         }
7215         var result;
7216         try {
7217             result = o.reader.read(response);
7218         }catch(e){
7219             this.fireEvent("loadexception", this, o, response, e);
7220             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7221             return;
7222         }
7223         
7224         this.fireEvent("load", this, o, o.request.arg);
7225         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7226     },
7227
7228     // private
7229     update : function(dataSet){
7230
7231     },
7232
7233     // private
7234     updateResponse : function(dataSet){
7235
7236     }
7237 });/*
7238  * Based on:
7239  * Ext JS Library 1.1.1
7240  * Copyright(c) 2006-2007, Ext JS, LLC.
7241  *
7242  * Originally Released Under LGPL - original licence link has changed is not relivant.
7243  *
7244  * Fork - LGPL
7245  * <script type="text/javascript">
7246  */
7247
7248 /**
7249  * @class Roo.data.ScriptTagProxy
7250  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7251  * other than the originating domain of the running page.<br><br>
7252  * <p>
7253  * <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
7254  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7255  * <p>
7256  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7257  * source code that is used as the source inside a &lt;script> tag.<br><br>
7258  * <p>
7259  * In order for the browser to process the returned data, the server must wrap the data object
7260  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7261  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7262  * depending on whether the callback name was passed:
7263  * <p>
7264  * <pre><code>
7265 boolean scriptTag = false;
7266 String cb = request.getParameter("callback");
7267 if (cb != null) {
7268     scriptTag = true;
7269     response.setContentType("text/javascript");
7270 } else {
7271     response.setContentType("application/x-json");
7272 }
7273 Writer out = response.getWriter();
7274 if (scriptTag) {
7275     out.write(cb + "(");
7276 }
7277 out.print(dataBlock.toJsonString());
7278 if (scriptTag) {
7279     out.write(");");
7280 }
7281 </pre></code>
7282  *
7283  * @constructor
7284  * @param {Object} config A configuration object.
7285  */
7286 Roo.data.ScriptTagProxy = function(config){
7287     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7288     Roo.apply(this, config);
7289     this.head = document.getElementsByTagName("head")[0];
7290 };
7291
7292 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7293
7294 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7295     /**
7296      * @cfg {String} url The URL from which to request the data object.
7297      */
7298     /**
7299      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7300      */
7301     timeout : 30000,
7302     /**
7303      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7304      * the server the name of the callback function set up by the load call to process the returned data object.
7305      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7306      * javascript output which calls this named function passing the data object as its only parameter.
7307      */
7308     callbackParam : "callback",
7309     /**
7310      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7311      * name to the request.
7312      */
7313     nocache : true,
7314
7315     /**
7316      * Load data from the configured URL, read the data object into
7317      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7318      * process that block using the passed callback.
7319      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7320      * for the request to the remote server.
7321      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7322      * object into a block of Roo.data.Records.
7323      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7324      * The function must be passed <ul>
7325      * <li>The Record block object</li>
7326      * <li>The "arg" argument from the load function</li>
7327      * <li>A boolean success indicator</li>
7328      * </ul>
7329      * @param {Object} scope The scope in which to call the callback
7330      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7331      */
7332     load : function(params, reader, callback, scope, arg){
7333         if(this.fireEvent("beforeload", this, params) !== false){
7334
7335             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7336
7337             var url = this.url;
7338             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7339             if(this.nocache){
7340                 url += "&_dc=" + (new Date().getTime());
7341             }
7342             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7343             var trans = {
7344                 id : transId,
7345                 cb : "stcCallback"+transId,
7346                 scriptId : "stcScript"+transId,
7347                 params : params,
7348                 arg : arg,
7349                 url : url,
7350                 callback : callback,
7351                 scope : scope,
7352                 reader : reader
7353             };
7354             var conn = this;
7355
7356             window[trans.cb] = function(o){
7357                 conn.handleResponse(o, trans);
7358             };
7359
7360             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7361
7362             if(this.autoAbort !== false){
7363                 this.abort();
7364             }
7365
7366             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7367
7368             var script = document.createElement("script");
7369             script.setAttribute("src", url);
7370             script.setAttribute("type", "text/javascript");
7371             script.setAttribute("id", trans.scriptId);
7372             this.head.appendChild(script);
7373
7374             this.trans = trans;
7375         }else{
7376             callback.call(scope||this, null, arg, false);
7377         }
7378     },
7379
7380     // private
7381     isLoading : function(){
7382         return this.trans ? true : false;
7383     },
7384
7385     /**
7386      * Abort the current server request.
7387      */
7388     abort : function(){
7389         if(this.isLoading()){
7390             this.destroyTrans(this.trans);
7391         }
7392     },
7393
7394     // private
7395     destroyTrans : function(trans, isLoaded){
7396         this.head.removeChild(document.getElementById(trans.scriptId));
7397         clearTimeout(trans.timeoutId);
7398         if(isLoaded){
7399             window[trans.cb] = undefined;
7400             try{
7401                 delete window[trans.cb];
7402             }catch(e){}
7403         }else{
7404             // if hasn't been loaded, wait for load to remove it to prevent script error
7405             window[trans.cb] = function(){
7406                 window[trans.cb] = undefined;
7407                 try{
7408                     delete window[trans.cb];
7409                 }catch(e){}
7410             };
7411         }
7412     },
7413
7414     // private
7415     handleResponse : function(o, trans){
7416         this.trans = false;
7417         this.destroyTrans(trans, true);
7418         var result;
7419         try {
7420             result = trans.reader.readRecords(o);
7421         }catch(e){
7422             this.fireEvent("loadexception", this, o, trans.arg, e);
7423             trans.callback.call(trans.scope||window, null, trans.arg, false);
7424             return;
7425         }
7426         this.fireEvent("load", this, o, trans.arg);
7427         trans.callback.call(trans.scope||window, result, trans.arg, true);
7428     },
7429
7430     // private
7431     handleFailure : function(trans){
7432         this.trans = false;
7433         this.destroyTrans(trans, false);
7434         this.fireEvent("loadexception", this, null, trans.arg);
7435         trans.callback.call(trans.scope||window, null, trans.arg, false);
7436     }
7437 });/*
7438  * Based on:
7439  * Ext JS Library 1.1.1
7440  * Copyright(c) 2006-2007, Ext JS, LLC.
7441  *
7442  * Originally Released Under LGPL - original licence link has changed is not relivant.
7443  *
7444  * Fork - LGPL
7445  * <script type="text/javascript">
7446  */
7447
7448 /**
7449  * @class Roo.data.JsonReader
7450  * @extends Roo.data.DataReader
7451  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7452  * based on mappings in a provided Roo.data.Record constructor.
7453  * 
7454  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7455  * in the reply previously. 
7456  * 
7457  * <p>
7458  * Example code:
7459  * <pre><code>
7460 var RecordDef = Roo.data.Record.create([
7461     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7462     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7463 ]);
7464 var myReader = new Roo.data.JsonReader({
7465     totalProperty: "results",    // The property which contains the total dataset size (optional)
7466     root: "rows",                // The property which contains an Array of row objects
7467     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7468 }, RecordDef);
7469 </code></pre>
7470  * <p>
7471  * This would consume a JSON file like this:
7472  * <pre><code>
7473 { 'results': 2, 'rows': [
7474     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7475     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7476 }
7477 </code></pre>
7478  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7479  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7480  * paged from the remote server.
7481  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7482  * @cfg {String} root name of the property which contains the Array of row objects.
7483  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7484  * @constructor
7485  * Create a new JsonReader
7486  * @param {Object} meta Metadata configuration options
7487  * @param {Object} recordType Either an Array of field definition objects,
7488  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7489  */
7490 Roo.data.JsonReader = function(meta, recordType){
7491     
7492     meta = meta || {};
7493     // set some defaults:
7494     Roo.applyIf(meta, {
7495         totalProperty: 'total',
7496         successProperty : 'success',
7497         root : 'data',
7498         id : 'id'
7499     });
7500     
7501     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7502 };
7503 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7504     
7505     /**
7506      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7507      * Used by Store query builder to append _requestMeta to params.
7508      * 
7509      */
7510     metaFromRemote : false,
7511     /**
7512      * This method is only used by a DataProxy which has retrieved data from a remote server.
7513      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7514      * @return {Object} data A data block which is used by an Roo.data.Store object as
7515      * a cache of Roo.data.Records.
7516      */
7517     read : function(response){
7518         var json = response.responseText;
7519        
7520         var o = /* eval:var:o */ eval("("+json+")");
7521         if(!o) {
7522             throw {message: "JsonReader.read: Json object not found"};
7523         }
7524         
7525         if(o.metaData){
7526             
7527             delete this.ef;
7528             this.metaFromRemote = true;
7529             this.meta = o.metaData;
7530             this.recordType = Roo.data.Record.create(o.metaData.fields);
7531             this.onMetaChange(this.meta, this.recordType, o);
7532         }
7533         return this.readRecords(o);
7534     },
7535
7536     // private function a store will implement
7537     onMetaChange : function(meta, recordType, o){
7538
7539     },
7540
7541     /**
7542          * @ignore
7543          */
7544     simpleAccess: function(obj, subsc) {
7545         return obj[subsc];
7546     },
7547
7548         /**
7549          * @ignore
7550          */
7551     getJsonAccessor: function(){
7552         var re = /[\[\.]/;
7553         return function(expr) {
7554             try {
7555                 return(re.test(expr))
7556                     ? new Function("obj", "return obj." + expr)
7557                     : function(obj){
7558                         return obj[expr];
7559                     };
7560             } catch(e){}
7561             return Roo.emptyFn;
7562         };
7563     }(),
7564
7565     /**
7566      * Create a data block containing Roo.data.Records from an XML document.
7567      * @param {Object} o An object which contains an Array of row objects in the property specified
7568      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7569      * which contains the total size of the dataset.
7570      * @return {Object} data A data block which is used by an Roo.data.Store object as
7571      * a cache of Roo.data.Records.
7572      */
7573     readRecords : function(o){
7574         /**
7575          * After any data loads, the raw JSON data is available for further custom processing.
7576          * @type Object
7577          */
7578         this.o = o;
7579         var s = this.meta, Record = this.recordType,
7580             f = Record.prototype.fields, fi = f.items, fl = f.length;
7581
7582 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7583         if (!this.ef) {
7584             if(s.totalProperty) {
7585                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7586                 }
7587                 if(s.successProperty) {
7588                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7589                 }
7590                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7591                 if (s.id) {
7592                         var g = this.getJsonAccessor(s.id);
7593                         this.getId = function(rec) {
7594                                 var r = g(rec);
7595                                 return (r === undefined || r === "") ? null : r;
7596                         };
7597                 } else {
7598                         this.getId = function(){return null;};
7599                 }
7600             this.ef = [];
7601             for(var jj = 0; jj < fl; jj++){
7602                 f = fi[jj];
7603                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7604                 this.ef[jj] = this.getJsonAccessor(map);
7605             }
7606         }
7607
7608         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7609         if(s.totalProperty){
7610             var vt = parseInt(this.getTotal(o), 10);
7611             if(!isNaN(vt)){
7612                 totalRecords = vt;
7613             }
7614         }
7615         if(s.successProperty){
7616             var vs = this.getSuccess(o);
7617             if(vs === false || vs === 'false'){
7618                 success = false;
7619             }
7620         }
7621         var records = [];
7622             for(var i = 0; i < c; i++){
7623                     var n = root[i];
7624                 var values = {};
7625                 var id = this.getId(n);
7626                 for(var j = 0; j < fl; j++){
7627                     f = fi[j];
7628                 var v = this.ef[j](n);
7629                 if (!f.convert) {
7630                     Roo.log('missing convert for ' + f.name);
7631                     Roo.log(f);
7632                     continue;
7633                 }
7634                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7635                 }
7636                 var record = new Record(values, id);
7637                 record.json = n;
7638                 records[i] = record;
7639             }
7640             return {
7641             raw : o,
7642                 success : success,
7643                 records : records,
7644                 totalRecords : totalRecords
7645             };
7646     }
7647 });/*
7648  * Based on:
7649  * Ext JS Library 1.1.1
7650  * Copyright(c) 2006-2007, Ext JS, LLC.
7651  *
7652  * Originally Released Under LGPL - original licence link has changed is not relivant.
7653  *
7654  * Fork - LGPL
7655  * <script type="text/javascript">
7656  */
7657
7658 /**
7659  * @class Roo.data.ArrayReader
7660  * @extends Roo.data.DataReader
7661  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7662  * Each element of that Array represents a row of data fields. The
7663  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7664  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7665  * <p>
7666  * Example code:.
7667  * <pre><code>
7668 var RecordDef = Roo.data.Record.create([
7669     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7670     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7671 ]);
7672 var myReader = new Roo.data.ArrayReader({
7673     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7674 }, RecordDef);
7675 </code></pre>
7676  * <p>
7677  * This would consume an Array like this:
7678  * <pre><code>
7679 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7680   </code></pre>
7681  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7682  * @constructor
7683  * Create a new JsonReader
7684  * @param {Object} meta Metadata configuration options.
7685  * @param {Object} recordType Either an Array of field definition objects
7686  * as specified to {@link Roo.data.Record#create},
7687  * or an {@link Roo.data.Record} object
7688  * created using {@link Roo.data.Record#create}.
7689  */
7690 Roo.data.ArrayReader = function(meta, recordType){
7691     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7692 };
7693
7694 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7695     /**
7696      * Create a data block containing Roo.data.Records from an XML document.
7697      * @param {Object} o An Array of row objects which represents the dataset.
7698      * @return {Object} data A data block which is used by an Roo.data.Store object as
7699      * a cache of Roo.data.Records.
7700      */
7701     readRecords : function(o){
7702         var sid = this.meta ? this.meta.id : null;
7703         var recordType = this.recordType, fields = recordType.prototype.fields;
7704         var records = [];
7705         var root = o;
7706             for(var i = 0; i < root.length; i++){
7707                     var n = root[i];
7708                 var values = {};
7709                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7710                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7711                 var f = fields.items[j];
7712                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7713                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7714                 v = f.convert(v);
7715                 values[f.name] = v;
7716             }
7717                 var record = new recordType(values, id);
7718                 record.json = n;
7719                 records[records.length] = record;
7720             }
7721             return {
7722                 records : records,
7723                 totalRecords : records.length
7724             };
7725     }
7726 });/*
7727  * - LGPL
7728  * * 
7729  */
7730
7731 /**
7732  * @class Roo.bootstrap.ComboBox
7733  * @extends Roo.bootstrap.TriggerField
7734  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7735  * @cfg {Boolean} append (true|false) default false
7736  * @constructor
7737  * Create a new ComboBox.
7738  * @param {Object} config Configuration options
7739  */
7740 Roo.bootstrap.ComboBox = function(config){
7741     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7742     this.addEvents({
7743         /**
7744          * @event expand
7745          * Fires when the dropdown list is expanded
7746              * @param {Roo.bootstrap.ComboBox} combo This combo box
7747              */
7748         'expand' : true,
7749         /**
7750          * @event collapse
7751          * Fires when the dropdown list is collapsed
7752              * @param {Roo.bootstrap.ComboBox} combo This combo box
7753              */
7754         'collapse' : true,
7755         /**
7756          * @event beforeselect
7757          * Fires before a list item is selected. Return false to cancel the selection.
7758              * @param {Roo.bootstrap.ComboBox} combo This combo box
7759              * @param {Roo.data.Record} record The data record returned from the underlying store
7760              * @param {Number} index The index of the selected item in the dropdown list
7761              */
7762         'beforeselect' : true,
7763         /**
7764          * @event select
7765          * Fires when a list item is selected
7766              * @param {Roo.bootstrap.ComboBox} combo This combo box
7767              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7768              * @param {Number} index The index of the selected item in the dropdown list
7769              */
7770         'select' : true,
7771         /**
7772          * @event beforequery
7773          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7774          * The event object passed has these properties:
7775              * @param {Roo.bootstrap.ComboBox} combo This combo box
7776              * @param {String} query The query
7777              * @param {Boolean} forceAll true to force "all" query
7778              * @param {Boolean} cancel true to cancel the query
7779              * @param {Object} e The query event object
7780              */
7781         'beforequery': true,
7782          /**
7783          * @event add
7784          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7785              * @param {Roo.bootstrap.ComboBox} combo This combo box
7786              */
7787         'add' : true,
7788         /**
7789          * @event edit
7790          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7791              * @param {Roo.bootstrap.ComboBox} combo This combo box
7792              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7793              */
7794         'edit' : true,
7795         /**
7796          * @event remove
7797          * Fires when the remove value from the combobox array
7798              * @param {Roo.bootstrap.ComboBox} combo This combo box
7799              */
7800         'remove' : true
7801         
7802     });
7803     
7804     
7805     this.selectedIndex = -1;
7806     if(this.mode == 'local'){
7807         if(config.queryDelay === undefined){
7808             this.queryDelay = 10;
7809         }
7810         if(config.minChars === undefined){
7811             this.minChars = 0;
7812         }
7813     }
7814 };
7815
7816 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7817      
7818     /**
7819      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7820      * rendering into an Roo.Editor, defaults to false)
7821      */
7822     /**
7823      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7824      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7825      */
7826     /**
7827      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7828      */
7829     /**
7830      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7831      * the dropdown list (defaults to undefined, with no header element)
7832      */
7833
7834      /**
7835      * @cfg {String/Roo.Template} tpl The template to use to render the output
7836      */
7837      
7838      /**
7839      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7840      */
7841     listWidth: undefined,
7842     /**
7843      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7844      * mode = 'remote' or 'text' if mode = 'local')
7845      */
7846     displayField: undefined,
7847     /**
7848      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7849      * mode = 'remote' or 'value' if mode = 'local'). 
7850      * Note: use of a valueField requires the user make a selection
7851      * in order for a value to be mapped.
7852      */
7853     valueField: undefined,
7854     
7855     
7856     /**
7857      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7858      * field's data value (defaults to the underlying DOM element's name)
7859      */
7860     hiddenName: undefined,
7861     /**
7862      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7863      */
7864     listClass: '',
7865     /**
7866      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7867      */
7868     selectedClass: 'active',
7869     
7870     /**
7871      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7872      */
7873     shadow:'sides',
7874     /**
7875      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7876      * anchor positions (defaults to 'tl-bl')
7877      */
7878     listAlign: 'tl-bl?',
7879     /**
7880      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7881      */
7882     maxHeight: 300,
7883     /**
7884      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7885      * query specified by the allQuery config option (defaults to 'query')
7886      */
7887     triggerAction: 'query',
7888     /**
7889      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7890      * (defaults to 4, does not apply if editable = false)
7891      */
7892     minChars : 4,
7893     /**
7894      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7895      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7896      */
7897     typeAhead: false,
7898     /**
7899      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7900      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7901      */
7902     queryDelay: 500,
7903     /**
7904      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7905      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7906      */
7907     pageSize: 0,
7908     /**
7909      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7910      * when editable = true (defaults to false)
7911      */
7912     selectOnFocus:false,
7913     /**
7914      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7915      */
7916     queryParam: 'query',
7917     /**
7918      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7919      * when mode = 'remote' (defaults to 'Loading...')
7920      */
7921     loadingText: 'Loading...',
7922     /**
7923      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7924      */
7925     resizable: false,
7926     /**
7927      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7928      */
7929     handleHeight : 8,
7930     /**
7931      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7932      * traditional select (defaults to true)
7933      */
7934     editable: true,
7935     /**
7936      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7937      */
7938     allQuery: '',
7939     /**
7940      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7941      */
7942     mode: 'remote',
7943     /**
7944      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7945      * listWidth has a higher value)
7946      */
7947     minListWidth : 70,
7948     /**
7949      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7950      * allow the user to set arbitrary text into the field (defaults to false)
7951      */
7952     forceSelection:false,
7953     /**
7954      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7955      * if typeAhead = true (defaults to 250)
7956      */
7957     typeAheadDelay : 250,
7958     /**
7959      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7960      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7961      */
7962     valueNotFoundText : undefined,
7963     /**
7964      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7965      */
7966     blockFocus : false,
7967     
7968     /**
7969      * @cfg {Boolean} disableClear Disable showing of clear button.
7970      */
7971     disableClear : false,
7972     /**
7973      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7974      */
7975     alwaysQuery : false,
7976     
7977     /**
7978      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7979      */
7980     multiple : false,
7981     
7982     //private
7983     addicon : false,
7984     editicon: false,
7985     
7986     page: 0,
7987     hasQuery: false,
7988     append: false,
7989     loadNext: false,
7990     item: [],
7991     
7992     // element that contains real text value.. (when hidden is used..)
7993      
7994     // private
7995     initEvents: function(){
7996         
7997         if (!this.store) {
7998             throw "can not find store for combo";
7999         }
8000         this.store = Roo.factory(this.store, Roo.data);
8001         
8002         
8003         
8004         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8005         
8006         
8007         if(this.hiddenName){
8008             
8009             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8010             
8011             this.hiddenField.dom.value =
8012                 this.hiddenValue !== undefined ? this.hiddenValue :
8013                 this.value !== undefined ? this.value : '';
8014
8015             // prevent input submission
8016             this.el.dom.removeAttribute('name');
8017             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8018              
8019              
8020         }
8021         //if(Roo.isGecko){
8022         //    this.el.dom.setAttribute('autocomplete', 'off');
8023         //}
8024
8025         var cls = 'x-combo-list';
8026         this.list = this.el.select('ul.dropdown-menu',true).first();
8027
8028         //this.list = new Roo.Layer({
8029         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8030         //});
8031         
8032         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8033         this.list.setWidth(lw);
8034         
8035         this.list.on('mouseover', this.onViewOver, this);
8036         this.list.on('mousemove', this.onViewMove, this);
8037         
8038         this.list.on('scroll', this.onViewScroll, this);
8039         
8040         /*
8041         this.list.swallowEvent('mousewheel');
8042         this.assetHeight = 0;
8043
8044         if(this.title){
8045             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8046             this.assetHeight += this.header.getHeight();
8047         }
8048
8049         this.innerList = this.list.createChild({cls:cls+'-inner'});
8050         this.innerList.on('mouseover', this.onViewOver, this);
8051         this.innerList.on('mousemove', this.onViewMove, this);
8052         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8053         
8054         if(this.allowBlank && !this.pageSize && !this.disableClear){
8055             this.footer = this.list.createChild({cls:cls+'-ft'});
8056             this.pageTb = new Roo.Toolbar(this.footer);
8057            
8058         }
8059         if(this.pageSize){
8060             this.footer = this.list.createChild({cls:cls+'-ft'});
8061             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8062                     {pageSize: this.pageSize});
8063             
8064         }
8065         
8066         if (this.pageTb && this.allowBlank && !this.disableClear) {
8067             var _this = this;
8068             this.pageTb.add(new Roo.Toolbar.Fill(), {
8069                 cls: 'x-btn-icon x-btn-clear',
8070                 text: '&#160;',
8071                 handler: function()
8072                 {
8073                     _this.collapse();
8074                     _this.clearValue();
8075                     _this.onSelect(false, -1);
8076                 }
8077             });
8078         }
8079         if (this.footer) {
8080             this.assetHeight += this.footer.getHeight();
8081         }
8082         */
8083             
8084         if(!this.tpl){
8085             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8086         }
8087
8088         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8089             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8090         });
8091         //this.view.wrapEl.setDisplayed(false);
8092         this.view.on('click', this.onViewClick, this);
8093         
8094         
8095         
8096         this.store.on('beforeload', this.onBeforeLoad, this);
8097         this.store.on('load', this.onLoad, this);
8098         this.store.on('loadexception', this.onLoadException, this);
8099         /*
8100         if(this.resizable){
8101             this.resizer = new Roo.Resizable(this.list,  {
8102                pinned:true, handles:'se'
8103             });
8104             this.resizer.on('resize', function(r, w, h){
8105                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8106                 this.listWidth = w;
8107                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8108                 this.restrictHeight();
8109             }, this);
8110             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8111         }
8112         */
8113         if(!this.editable){
8114             this.editable = true;
8115             this.setEditable(false);
8116         }
8117         
8118         /*
8119         
8120         if (typeof(this.events.add.listeners) != 'undefined') {
8121             
8122             this.addicon = this.wrap.createChild(
8123                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8124        
8125             this.addicon.on('click', function(e) {
8126                 this.fireEvent('add', this);
8127             }, this);
8128         }
8129         if (typeof(this.events.edit.listeners) != 'undefined') {
8130             
8131             this.editicon = this.wrap.createChild(
8132                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8133             if (this.addicon) {
8134                 this.editicon.setStyle('margin-left', '40px');
8135             }
8136             this.editicon.on('click', function(e) {
8137                 
8138                 // we fire even  if inothing is selected..
8139                 this.fireEvent('edit', this, this.lastData );
8140                 
8141             }, this);
8142         }
8143         */
8144         
8145         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8146             "up" : function(e){
8147                 this.inKeyMode = true;
8148                 this.selectPrev();
8149             },
8150
8151             "down" : function(e){
8152                 if(!this.isExpanded()){
8153                     this.onTriggerClick();
8154                 }else{
8155                     this.inKeyMode = true;
8156                     this.selectNext();
8157                 }
8158             },
8159
8160             "enter" : function(e){
8161                 this.onViewClick();
8162                 //return true;
8163             },
8164
8165             "esc" : function(e){
8166                 this.collapse();
8167             },
8168
8169             "tab" : function(e){
8170                 this.collapse();
8171                 
8172                 if(this.fireEvent("specialkey", this, e)){
8173                     this.onViewClick(false);
8174                 }
8175                 
8176                 return true;
8177             },
8178
8179             scope : this,
8180
8181             doRelay : function(foo, bar, hname){
8182                 if(hname == 'down' || this.scope.isExpanded()){
8183                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8184                 }
8185                 return true;
8186             },
8187
8188             forceKeyDown: true
8189         });
8190         
8191         
8192         this.queryDelay = Math.max(this.queryDelay || 10,
8193                 this.mode == 'local' ? 10 : 250);
8194         
8195         
8196         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8197         
8198         if(this.typeAhead){
8199             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8200         }
8201         if(this.editable !== false){
8202             this.inputEl().on("keyup", this.onKeyUp, this);
8203         }
8204         if(this.forceSelection){
8205             this.on('blur', this.doForce, this);
8206         }
8207         
8208         if(this.multiple){
8209             this.choices = this.el.select('ul.select2-choices', true).first();
8210             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8211         }
8212     },
8213
8214     onDestroy : function(){
8215         if(this.view){
8216             this.view.setStore(null);
8217             this.view.el.removeAllListeners();
8218             this.view.el.remove();
8219             this.view.purgeListeners();
8220         }
8221         if(this.list){
8222             this.list.dom.innerHTML  = '';
8223         }
8224         if(this.store){
8225             this.store.un('beforeload', this.onBeforeLoad, this);
8226             this.store.un('load', this.onLoad, this);
8227             this.store.un('loadexception', this.onLoadException, this);
8228         }
8229         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8230     },
8231
8232     // private
8233     fireKey : function(e){
8234         if(e.isNavKeyPress() && !this.list.isVisible()){
8235             this.fireEvent("specialkey", this, e);
8236         }
8237     },
8238
8239     // private
8240     onResize: function(w, h){
8241 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8242 //        
8243 //        if(typeof w != 'number'){
8244 //            // we do not handle it!?!?
8245 //            return;
8246 //        }
8247 //        var tw = this.trigger.getWidth();
8248 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8249 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8250 //        var x = w - tw;
8251 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8252 //            
8253 //        //this.trigger.setStyle('left', x+'px');
8254 //        
8255 //        if(this.list && this.listWidth === undefined){
8256 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8257 //            this.list.setWidth(lw);
8258 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8259 //        }
8260         
8261     
8262         
8263     },
8264
8265     /**
8266      * Allow or prevent the user from directly editing the field text.  If false is passed,
8267      * the user will only be able to select from the items defined in the dropdown list.  This method
8268      * is the runtime equivalent of setting the 'editable' config option at config time.
8269      * @param {Boolean} value True to allow the user to directly edit the field text
8270      */
8271     setEditable : function(value){
8272         if(value == this.editable){
8273             return;
8274         }
8275         this.editable = value;
8276         if(!value){
8277             this.inputEl().dom.setAttribute('readOnly', true);
8278             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8279             this.inputEl().addClass('x-combo-noedit');
8280         }else{
8281             this.inputEl().dom.setAttribute('readOnly', false);
8282             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8283             this.inputEl().removeClass('x-combo-noedit');
8284         }
8285     },
8286
8287     // private
8288     
8289     onBeforeLoad : function(combo,opts){
8290         if(!this.hasFocus){
8291             return;
8292         }
8293          if (!opts.add) {
8294             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8295          }
8296         this.restrictHeight();
8297         this.selectedIndex = -1;
8298     },
8299
8300     // private
8301     onLoad : function(){
8302         
8303         this.hasQuery = false;
8304         
8305         if(!this.hasFocus){
8306             return;
8307         }
8308         
8309         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8310             this.loading.hide();
8311         }
8312         
8313         if(this.store.getCount() > 0){
8314             this.expand();
8315             this.restrictHeight();
8316             if(this.lastQuery == this.allQuery){
8317                 if(this.editable){
8318                     this.inputEl().dom.select();
8319                 }
8320                 if(!this.selectByValue(this.value, true)){
8321                     this.select(0, true);
8322                 }
8323             }else{
8324                 this.selectNext();
8325                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8326                     this.taTask.delay(this.typeAheadDelay);
8327                 }
8328             }
8329         }else{
8330             this.onEmptyResults();
8331         }
8332         
8333         //this.el.focus();
8334     },
8335     // private
8336     onLoadException : function()
8337     {
8338         this.hasQuery = false;
8339         
8340         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8341             this.loading.hide();
8342         }
8343         
8344         this.collapse();
8345         Roo.log(this.store.reader.jsonData);
8346         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8347             // fixme
8348             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8349         }
8350         
8351         
8352     },
8353     // private
8354     onTypeAhead : function(){
8355         if(this.store.getCount() > 0){
8356             var r = this.store.getAt(0);
8357             var newValue = r.data[this.displayField];
8358             var len = newValue.length;
8359             var selStart = this.getRawValue().length;
8360             
8361             if(selStart != len){
8362                 this.setRawValue(newValue);
8363                 this.selectText(selStart, newValue.length);
8364             }
8365         }
8366     },
8367
8368     // private
8369     onSelect : function(record, index){
8370         
8371         if(this.fireEvent('beforeselect', this, record, index) !== false){
8372         
8373             this.setFromData(index > -1 ? record.data : false);
8374             
8375             this.collapse();
8376             this.fireEvent('select', this, record, index);
8377         }
8378     },
8379
8380     /**
8381      * Returns the currently selected field value or empty string if no value is set.
8382      * @return {String} value The selected value
8383      */
8384     getValue : function(){
8385         
8386         if(this.multiple){
8387             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8388         }
8389         
8390         if(this.valueField){
8391             return typeof this.value != 'undefined' ? this.value : '';
8392         }else{
8393             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8394         }
8395     },
8396
8397     /**
8398      * Clears any text/value currently set in the field
8399      */
8400     clearValue : function(){
8401         if(this.hiddenField){
8402             this.hiddenField.dom.value = '';
8403         }
8404         this.value = '';
8405         this.setRawValue('');
8406         this.lastSelectionText = '';
8407         
8408     },
8409
8410     /**
8411      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8412      * will be displayed in the field.  If the value does not match the data value of an existing item,
8413      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8414      * Otherwise the field will be blank (although the value will still be set).
8415      * @param {String} value The value to match
8416      */
8417     setValue : function(v){
8418         if(this.multiple){
8419             this.syncValue();
8420             return;
8421         }
8422         
8423         var text = v;
8424         if(this.valueField){
8425             var r = this.findRecord(this.valueField, v);
8426             if(r){
8427                 text = r.data[this.displayField];
8428             }else if(this.valueNotFoundText !== undefined){
8429                 text = this.valueNotFoundText;
8430             }
8431         }
8432         this.lastSelectionText = text;
8433         if(this.hiddenField){
8434             this.hiddenField.dom.value = v;
8435         }
8436         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8437         this.value = v;
8438     },
8439     /**
8440      * @property {Object} the last set data for the element
8441      */
8442     
8443     lastData : false,
8444     /**
8445      * Sets the value of the field based on a object which is related to the record format for the store.
8446      * @param {Object} value the value to set as. or false on reset?
8447      */
8448     setFromData : function(o){
8449         
8450         if(this.multiple){
8451             this.addItem(o);
8452             return;
8453         }
8454             
8455         var dv = ''; // display value
8456         var vv = ''; // value value..
8457         this.lastData = o;
8458         if (this.displayField) {
8459             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8460         } else {
8461             // this is an error condition!!!
8462             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8463         }
8464         
8465         if(this.valueField){
8466             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8467         }
8468         
8469         if(this.hiddenField){
8470             this.hiddenField.dom.value = vv;
8471             
8472             this.lastSelectionText = dv;
8473             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8474             this.value = vv;
8475             return;
8476         }
8477         // no hidden field.. - we store the value in 'value', but still display
8478         // display field!!!!
8479         this.lastSelectionText = dv;
8480         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8481         this.value = vv;
8482         
8483         
8484     },
8485     // private
8486     reset : function(){
8487         // overridden so that last data is reset..
8488         this.setValue(this.originalValue);
8489         this.clearInvalid();
8490         this.lastData = false;
8491         if (this.view) {
8492             this.view.clearSelections();
8493         }
8494     },
8495     // private
8496     findRecord : function(prop, value){
8497         var record;
8498         if(this.store.getCount() > 0){
8499             this.store.each(function(r){
8500                 if(r.data[prop] == value){
8501                     record = r;
8502                     return false;
8503                 }
8504                 return true;
8505             });
8506         }
8507         return record;
8508     },
8509     
8510     getName: function()
8511     {
8512         // returns hidden if it's set..
8513         if (!this.rendered) {return ''};
8514         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8515         
8516     },
8517     // private
8518     onViewMove : function(e, t){
8519         this.inKeyMode = false;
8520     },
8521
8522     // private
8523     onViewOver : function(e, t){
8524         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8525             return;
8526         }
8527         var item = this.view.findItemFromChild(t);
8528         if(item){
8529             var index = this.view.indexOf(item);
8530             this.select(index, false);
8531         }
8532     },
8533
8534     // private
8535     onViewClick : function(doFocus)
8536     {
8537         var index = this.view.getSelectedIndexes()[0];
8538         var r = this.store.getAt(index);
8539         if(r){
8540             this.onSelect(r, index);
8541         }
8542         if(doFocus !== false && !this.blockFocus){
8543             this.inputEl().focus();
8544         }
8545     },
8546
8547     // private
8548     restrictHeight : function(){
8549         //this.innerList.dom.style.height = '';
8550         //var inner = this.innerList.dom;
8551         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8552         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8553         //this.list.beginUpdate();
8554         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8555         this.list.alignTo(this.inputEl(), this.listAlign);
8556         //this.list.endUpdate();
8557     },
8558
8559     // private
8560     onEmptyResults : function(){
8561         this.collapse();
8562     },
8563
8564     /**
8565      * Returns true if the dropdown list is expanded, else false.
8566      */
8567     isExpanded : function(){
8568         return this.list.isVisible();
8569     },
8570
8571     /**
8572      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8573      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8574      * @param {String} value The data value of the item to select
8575      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8576      * selected item if it is not currently in view (defaults to true)
8577      * @return {Boolean} True if the value matched an item in the list, else false
8578      */
8579     selectByValue : function(v, scrollIntoView){
8580         if(v !== undefined && v !== null){
8581             var r = this.findRecord(this.valueField || this.displayField, v);
8582             if(r){
8583                 this.select(this.store.indexOf(r), scrollIntoView);
8584                 return true;
8585             }
8586         }
8587         return false;
8588     },
8589
8590     /**
8591      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8592      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8593      * @param {Number} index The zero-based index of the list item to select
8594      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8595      * selected item if it is not currently in view (defaults to true)
8596      */
8597     select : function(index, scrollIntoView){
8598         this.selectedIndex = index;
8599         this.view.select(index);
8600         if(scrollIntoView !== false){
8601             var el = this.view.getNode(index);
8602             if(el){
8603                 //this.innerList.scrollChildIntoView(el, false);
8604                 
8605             }
8606         }
8607     },
8608
8609     // private
8610     selectNext : function(){
8611         var ct = this.store.getCount();
8612         if(ct > 0){
8613             if(this.selectedIndex == -1){
8614                 this.select(0);
8615             }else if(this.selectedIndex < ct-1){
8616                 this.select(this.selectedIndex+1);
8617             }
8618         }
8619     },
8620
8621     // private
8622     selectPrev : function(){
8623         var ct = this.store.getCount();
8624         if(ct > 0){
8625             if(this.selectedIndex == -1){
8626                 this.select(0);
8627             }else if(this.selectedIndex != 0){
8628                 this.select(this.selectedIndex-1);
8629             }
8630         }
8631     },
8632
8633     // private
8634     onKeyUp : function(e){
8635         if(this.editable !== false && !e.isSpecialKey()){
8636             this.lastKey = e.getKey();
8637             this.dqTask.delay(this.queryDelay);
8638         }
8639     },
8640
8641     // private
8642     validateBlur : function(){
8643         return !this.list || !this.list.isVisible();   
8644     },
8645
8646     // private
8647     initQuery : function(){
8648         this.doQuery(this.getRawValue());
8649     },
8650
8651     // private
8652     doForce : function(){
8653         if(this.el.dom.value.length > 0){
8654             this.el.dom.value =
8655                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8656              
8657         }
8658     },
8659
8660     /**
8661      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8662      * query allowing the query action to be canceled if needed.
8663      * @param {String} query The SQL query to execute
8664      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8665      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8666      * saved in the current store (defaults to false)
8667      */
8668     doQuery : function(q, forceAll){
8669         
8670         if(q === undefined || q === null){
8671             q = '';
8672         }
8673         var qe = {
8674             query: q,
8675             forceAll: forceAll,
8676             combo: this,
8677             cancel:false
8678         };
8679         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8680             return false;
8681         }
8682         q = qe.query;
8683         
8684         forceAll = qe.forceAll;
8685         if(forceAll === true || (q.length >= this.minChars)){
8686             
8687             this.hasQuery = true;
8688             
8689             if(this.lastQuery != q || this.alwaysQuery){
8690                 this.lastQuery = q;
8691                 if(this.mode == 'local'){
8692                     this.selectedIndex = -1;
8693                     if(forceAll){
8694                         this.store.clearFilter();
8695                     }else{
8696                         this.store.filter(this.displayField, q);
8697                     }
8698                     this.onLoad();
8699                 }else{
8700                     this.store.baseParams[this.queryParam] = q;
8701                     
8702                     var options = {params : this.getParams(q)};
8703                     
8704                     if(this.loadNext){
8705                         options.add = true;
8706                         options.params.start = this.page * this.pageSize;
8707                     }
8708                     
8709                     this.store.load(options);
8710                     this.expand();
8711                 }
8712             }else{
8713                 this.selectedIndex = -1;
8714                 this.onLoad();   
8715             }
8716         }
8717         
8718         this.loadNext = false;
8719     },
8720
8721     // private
8722     getParams : function(q){
8723         var p = {};
8724         //p[this.queryParam] = q;
8725         
8726         if(this.pageSize){
8727             p.start = 0;
8728             p.limit = this.pageSize;
8729         }
8730         return p;
8731     },
8732
8733     /**
8734      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8735      */
8736     collapse : function(){
8737         if(!this.isExpanded()){
8738             return;
8739         }
8740         
8741         this.list.hide();
8742         Roo.get(document).un('mousedown', this.collapseIf, this);
8743         Roo.get(document).un('mousewheel', this.collapseIf, this);
8744         if (!this.editable) {
8745             Roo.get(document).un('keydown', this.listKeyPress, this);
8746         }
8747         this.fireEvent('collapse', this);
8748     },
8749
8750     // private
8751     collapseIf : function(e){
8752         var in_combo  = e.within(this.el);
8753         var in_list =  e.within(this.list);
8754         
8755         if (in_combo || in_list) {
8756             //e.stopPropagation();
8757             return;
8758         }
8759
8760         this.collapse();
8761         
8762     },
8763
8764     /**
8765      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8766      */
8767     expand : function(){
8768        
8769         if(this.isExpanded() || !this.hasFocus){
8770             return;
8771         }
8772          Roo.log('expand');
8773         this.list.alignTo(this.inputEl(), this.listAlign);
8774         this.list.show();
8775         Roo.get(document).on('mousedown', this.collapseIf, this);
8776         Roo.get(document).on('mousewheel', this.collapseIf, this);
8777         if (!this.editable) {
8778             Roo.get(document).on('keydown', this.listKeyPress, this);
8779         }
8780         
8781         this.fireEvent('expand', this);
8782     },
8783
8784     // private
8785     // Implements the default empty TriggerField.onTriggerClick function
8786     onTriggerClick : function()
8787     {
8788         Roo.log('trigger click');
8789         
8790         if(this.disabled){
8791             return;
8792         }
8793         
8794         this.page = 0;
8795         this.loadNext = false;
8796         
8797         if(this.isExpanded()){
8798             this.collapse();
8799             if (!this.blockFocus) {
8800                 this.inputEl().focus();
8801             }
8802             
8803         }else {
8804             this.hasFocus = true;
8805             if(this.triggerAction == 'all') {
8806                 this.doQuery(this.allQuery, true);
8807             } else {
8808                 this.doQuery(this.getRawValue());
8809             }
8810             if (!this.blockFocus) {
8811                 this.inputEl().focus();
8812             }
8813         }
8814     },
8815     listKeyPress : function(e)
8816     {
8817         //Roo.log('listkeypress');
8818         // scroll to first matching element based on key pres..
8819         if (e.isSpecialKey()) {
8820             return false;
8821         }
8822         var k = String.fromCharCode(e.getKey()).toUpperCase();
8823         //Roo.log(k);
8824         var match  = false;
8825         var csel = this.view.getSelectedNodes();
8826         var cselitem = false;
8827         if (csel.length) {
8828             var ix = this.view.indexOf(csel[0]);
8829             cselitem  = this.store.getAt(ix);
8830             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8831                 cselitem = false;
8832             }
8833             
8834         }
8835         
8836         this.store.each(function(v) { 
8837             if (cselitem) {
8838                 // start at existing selection.
8839                 if (cselitem.id == v.id) {
8840                     cselitem = false;
8841                 }
8842                 return true;
8843             }
8844                 
8845             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8846                 match = this.store.indexOf(v);
8847                 return false;
8848             }
8849             return true;
8850         }, this);
8851         
8852         if (match === false) {
8853             return true; // no more action?
8854         }
8855         // scroll to?
8856         this.view.select(match);
8857         var sn = Roo.get(this.view.getSelectedNodes()[0])
8858         //sn.scrollIntoView(sn.dom.parentNode, false);
8859     },
8860     
8861     onViewScroll : function(e, t){
8862         
8863         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8864             return;
8865         }
8866         
8867         this.hasQuery = true;
8868         
8869         this.loading = this.list.select('.loading', true).first();
8870         
8871         if(this.loading === null){
8872             this.list.createChild({
8873                 tag: 'div',
8874                 cls: 'loading select2-more-results select2-active',
8875                 html: 'Loading more results...'
8876             })
8877             
8878             this.loading = this.list.select('.loading', true).first();
8879             
8880             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8881             
8882             this.loading.hide();
8883         }
8884         
8885         this.loading.show();
8886         
8887         var _combo = this;
8888         
8889         this.page++;
8890         this.loadNext = true;
8891         
8892         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8893         
8894         return;
8895     },
8896     
8897     addItem : function(o)
8898     {   
8899         var dv = ''; // display value
8900         
8901         if (this.displayField) {
8902             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8903         } else {
8904             // this is an error condition!!!
8905             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8906         }
8907         
8908         if(!dv.length){
8909             return;
8910         }
8911         
8912         var choice = this.choices.createChild({
8913             tag: 'li',
8914             cls: 'select2-search-choice',
8915             cn: [
8916                 {
8917                     tag: 'div',
8918                     html: dv
8919                 },
8920                 {
8921                     tag: 'a',
8922                     href: '#',
8923                     cls: 'select2-search-choice-close',
8924                     tabindex: '-1'
8925                 }
8926             ]
8927             
8928         }, this.searchField);
8929         
8930         var close = choice.select('a.select2-search-choice-close', true).first()
8931         
8932         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8933         
8934         this.item.push(o);
8935         this.lastData = o;
8936         
8937         this.syncValue();
8938         
8939         this.inputEl().dom.value = '';
8940         
8941     },
8942     
8943     onRemoveItem : function(e, _self, o)
8944     {
8945         Roo.log('remove item');
8946         var index = this.item.indexOf(o.data) * 1;
8947         
8948         if( index < 0){
8949             Roo.log('not this item?!');
8950             return;
8951         }
8952         
8953         this.item.splice(index, 1);
8954         o.item.remove();
8955         
8956         this.syncValue();
8957         
8958         this.fireEvent('remove', this);
8959         
8960     },
8961     
8962     syncValue : function()
8963     {
8964         if(!this.item.length){
8965             this.clearValue();
8966             return;
8967         }
8968             
8969         var value = [];
8970         var _this = this;
8971         Roo.each(this.item, function(i){
8972             if(_this.valueField){
8973                 value.push(i[_this.valueField]);
8974                 return;
8975             }
8976
8977             value.push(i);
8978         });
8979
8980         this.value = value.join(',');
8981
8982         if(this.hiddenField){
8983             this.hiddenField.dom.value = this.value;
8984         }
8985     },
8986     
8987     clearItem : function()
8988     {
8989         if(!this.multiple){
8990             return;
8991         }
8992         
8993         this.item = [];
8994         
8995         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
8996            c.remove();
8997         });
8998         
8999         this.syncValue();
9000     }
9001     
9002     
9003
9004     /** 
9005     * @cfg {Boolean} grow 
9006     * @hide 
9007     */
9008     /** 
9009     * @cfg {Number} growMin 
9010     * @hide 
9011     */
9012     /** 
9013     * @cfg {Number} growMax 
9014     * @hide 
9015     */
9016     /**
9017      * @hide
9018      * @method autoSize
9019      */
9020 });
9021 /*
9022  * Based on:
9023  * Ext JS Library 1.1.1
9024  * Copyright(c) 2006-2007, Ext JS, LLC.
9025  *
9026  * Originally Released Under LGPL - original licence link has changed is not relivant.
9027  *
9028  * Fork - LGPL
9029  * <script type="text/javascript">
9030  */
9031
9032 /**
9033  * @class Roo.View
9034  * @extends Roo.util.Observable
9035  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9036  * This class also supports single and multi selection modes. <br>
9037  * Create a data model bound view:
9038  <pre><code>
9039  var store = new Roo.data.Store(...);
9040
9041  var view = new Roo.View({
9042     el : "my-element",
9043     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9044  
9045     singleSelect: true,
9046     selectedClass: "ydataview-selected",
9047     store: store
9048  });
9049
9050  // listen for node click?
9051  view.on("click", function(vw, index, node, e){
9052  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9053  });
9054
9055  // load XML data
9056  dataModel.load("foobar.xml");
9057  </code></pre>
9058  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9059  * <br><br>
9060  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9061  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9062  * 
9063  * Note: old style constructor is still suported (container, template, config)
9064  * 
9065  * @constructor
9066  * Create a new View
9067  * @param {Object} config The config object
9068  * 
9069  */
9070 Roo.View = function(config, depreciated_tpl, depreciated_config){
9071     
9072     if (typeof(depreciated_tpl) == 'undefined') {
9073         // new way.. - universal constructor.
9074         Roo.apply(this, config);
9075         this.el  = Roo.get(this.el);
9076     } else {
9077         // old format..
9078         this.el  = Roo.get(config);
9079         this.tpl = depreciated_tpl;
9080         Roo.apply(this, depreciated_config);
9081     }
9082     this.wrapEl  = this.el.wrap().wrap();
9083     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9084     
9085     
9086     if(typeof(this.tpl) == "string"){
9087         this.tpl = new Roo.Template(this.tpl);
9088     } else {
9089         // support xtype ctors..
9090         this.tpl = new Roo.factory(this.tpl, Roo);
9091     }
9092     
9093     
9094     this.tpl.compile();
9095    
9096   
9097     
9098      
9099     /** @private */
9100     this.addEvents({
9101         /**
9102          * @event beforeclick
9103          * Fires before a click is processed. Returns false to cancel the default action.
9104          * @param {Roo.View} this
9105          * @param {Number} index The index of the target node
9106          * @param {HTMLElement} node The target node
9107          * @param {Roo.EventObject} e The raw event object
9108          */
9109             "beforeclick" : true,
9110         /**
9111          * @event click
9112          * Fires when a template node is clicked.
9113          * @param {Roo.View} this
9114          * @param {Number} index The index of the target node
9115          * @param {HTMLElement} node The target node
9116          * @param {Roo.EventObject} e The raw event object
9117          */
9118             "click" : true,
9119         /**
9120          * @event dblclick
9121          * Fires when a template node is double clicked.
9122          * @param {Roo.View} this
9123          * @param {Number} index The index of the target node
9124          * @param {HTMLElement} node The target node
9125          * @param {Roo.EventObject} e The raw event object
9126          */
9127             "dblclick" : true,
9128         /**
9129          * @event contextmenu
9130          * Fires when a template node is right clicked.
9131          * @param {Roo.View} this
9132          * @param {Number} index The index of the target node
9133          * @param {HTMLElement} node The target node
9134          * @param {Roo.EventObject} e The raw event object
9135          */
9136             "contextmenu" : true,
9137         /**
9138          * @event selectionchange
9139          * Fires when the selected nodes change.
9140          * @param {Roo.View} this
9141          * @param {Array} selections Array of the selected nodes
9142          */
9143             "selectionchange" : true,
9144     
9145         /**
9146          * @event beforeselect
9147          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9148          * @param {Roo.View} this
9149          * @param {HTMLElement} node The node to be selected
9150          * @param {Array} selections Array of currently selected nodes
9151          */
9152             "beforeselect" : true,
9153         /**
9154          * @event preparedata
9155          * Fires on every row to render, to allow you to change the data.
9156          * @param {Roo.View} this
9157          * @param {Object} data to be rendered (change this)
9158          */
9159           "preparedata" : true
9160           
9161           
9162         });
9163
9164
9165
9166     this.el.on({
9167         "click": this.onClick,
9168         "dblclick": this.onDblClick,
9169         "contextmenu": this.onContextMenu,
9170         scope:this
9171     });
9172
9173     this.selections = [];
9174     this.nodes = [];
9175     this.cmp = new Roo.CompositeElementLite([]);
9176     if(this.store){
9177         this.store = Roo.factory(this.store, Roo.data);
9178         this.setStore(this.store, true);
9179     }
9180     
9181     if ( this.footer && this.footer.xtype) {
9182            
9183          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9184         
9185         this.footer.dataSource = this.store
9186         this.footer.container = fctr;
9187         this.footer = Roo.factory(this.footer, Roo);
9188         fctr.insertFirst(this.el);
9189         
9190         // this is a bit insane - as the paging toolbar seems to detach the el..
9191 //        dom.parentNode.parentNode.parentNode
9192          // they get detached?
9193     }
9194     
9195     
9196     Roo.View.superclass.constructor.call(this);
9197     
9198     
9199 };
9200
9201 Roo.extend(Roo.View, Roo.util.Observable, {
9202     
9203      /**
9204      * @cfg {Roo.data.Store} store Data store to load data from.
9205      */
9206     store : false,
9207     
9208     /**
9209      * @cfg {String|Roo.Element} el The container element.
9210      */
9211     el : '',
9212     
9213     /**
9214      * @cfg {String|Roo.Template} tpl The template used by this View 
9215      */
9216     tpl : false,
9217     /**
9218      * @cfg {String} dataName the named area of the template to use as the data area
9219      *                          Works with domtemplates roo-name="name"
9220      */
9221     dataName: false,
9222     /**
9223      * @cfg {String} selectedClass The css class to add to selected nodes
9224      */
9225     selectedClass : "x-view-selected",
9226      /**
9227      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9228      */
9229     emptyText : "",
9230     
9231     /**
9232      * @cfg {String} text to display on mask (default Loading)
9233      */
9234     mask : false,
9235     /**
9236      * @cfg {Boolean} multiSelect Allow multiple selection
9237      */
9238     multiSelect : false,
9239     /**
9240      * @cfg {Boolean} singleSelect Allow single selection
9241      */
9242     singleSelect:  false,
9243     
9244     /**
9245      * @cfg {Boolean} toggleSelect - selecting 
9246      */
9247     toggleSelect : false,
9248     
9249     /**
9250      * Returns the element this view is bound to.
9251      * @return {Roo.Element}
9252      */
9253     getEl : function(){
9254         return this.wrapEl;
9255     },
9256     
9257     
9258
9259     /**
9260      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9261      */
9262     refresh : function(){
9263         Roo.log('refresh');
9264         var t = this.tpl;
9265         
9266         // if we are using something like 'domtemplate', then
9267         // the what gets used is:
9268         // t.applySubtemplate(NAME, data, wrapping data..)
9269         // the outer template then get' applied with
9270         //     the store 'extra data'
9271         // and the body get's added to the
9272         //      roo-name="data" node?
9273         //      <span class='roo-tpl-{name}'></span> ?????
9274         
9275         
9276         
9277         this.clearSelections();
9278         this.el.update("");
9279         var html = [];
9280         var records = this.store.getRange();
9281         if(records.length < 1) {
9282             
9283             // is this valid??  = should it render a template??
9284             
9285             this.el.update(this.emptyText);
9286             return;
9287         }
9288         var el = this.el;
9289         if (this.dataName) {
9290             this.el.update(t.apply(this.store.meta)); //????
9291             el = this.el.child('.roo-tpl-' + this.dataName);
9292         }
9293         
9294         for(var i = 0, len = records.length; i < len; i++){
9295             var data = this.prepareData(records[i].data, i, records[i]);
9296             this.fireEvent("preparedata", this, data, i, records[i]);
9297             html[html.length] = Roo.util.Format.trim(
9298                 this.dataName ?
9299                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9300                     t.apply(data)
9301             );
9302         }
9303         
9304         
9305         
9306         el.update(html.join(""));
9307         this.nodes = el.dom.childNodes;
9308         this.updateIndexes(0);
9309     },
9310     
9311
9312     /**
9313      * Function to override to reformat the data that is sent to
9314      * the template for each node.
9315      * DEPRICATED - use the preparedata event handler.
9316      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9317      * a JSON object for an UpdateManager bound view).
9318      */
9319     prepareData : function(data, index, record)
9320     {
9321         this.fireEvent("preparedata", this, data, index, record);
9322         return data;
9323     },
9324
9325     onUpdate : function(ds, record){
9326          Roo.log('on update');   
9327         this.clearSelections();
9328         var index = this.store.indexOf(record);
9329         var n = this.nodes[index];
9330         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9331         n.parentNode.removeChild(n);
9332         this.updateIndexes(index, index);
9333     },
9334
9335     
9336     
9337 // --------- FIXME     
9338     onAdd : function(ds, records, index)
9339     {
9340         Roo.log(['on Add', ds, records, index] );        
9341         this.clearSelections();
9342         if(this.nodes.length == 0){
9343             this.refresh();
9344             return;
9345         }
9346         var n = this.nodes[index];
9347         for(var i = 0, len = records.length; i < len; i++){
9348             var d = this.prepareData(records[i].data, i, records[i]);
9349             if(n){
9350                 this.tpl.insertBefore(n, d);
9351             }else{
9352                 
9353                 this.tpl.append(this.el, d);
9354             }
9355         }
9356         this.updateIndexes(index);
9357     },
9358
9359     onRemove : function(ds, record, index){
9360         Roo.log('onRemove');
9361         this.clearSelections();
9362         var el = this.dataName  ?
9363             this.el.child('.roo-tpl-' + this.dataName) :
9364             this.el; 
9365         
9366         el.dom.removeChild(this.nodes[index]);
9367         this.updateIndexes(index);
9368     },
9369
9370     /**
9371      * Refresh an individual node.
9372      * @param {Number} index
9373      */
9374     refreshNode : function(index){
9375         this.onUpdate(this.store, this.store.getAt(index));
9376     },
9377
9378     updateIndexes : function(startIndex, endIndex){
9379         var ns = this.nodes;
9380         startIndex = startIndex || 0;
9381         endIndex = endIndex || ns.length - 1;
9382         for(var i = startIndex; i <= endIndex; i++){
9383             ns[i].nodeIndex = i;
9384         }
9385     },
9386
9387     /**
9388      * Changes the data store this view uses and refresh the view.
9389      * @param {Store} store
9390      */
9391     setStore : function(store, initial){
9392         if(!initial && this.store){
9393             this.store.un("datachanged", this.refresh);
9394             this.store.un("add", this.onAdd);
9395             this.store.un("remove", this.onRemove);
9396             this.store.un("update", this.onUpdate);
9397             this.store.un("clear", this.refresh);
9398             this.store.un("beforeload", this.onBeforeLoad);
9399             this.store.un("load", this.onLoad);
9400             this.store.un("loadexception", this.onLoad);
9401         }
9402         if(store){
9403           
9404             store.on("datachanged", this.refresh, this);
9405             store.on("add", this.onAdd, this);
9406             store.on("remove", this.onRemove, this);
9407             store.on("update", this.onUpdate, this);
9408             store.on("clear", this.refresh, this);
9409             store.on("beforeload", this.onBeforeLoad, this);
9410             store.on("load", this.onLoad, this);
9411             store.on("loadexception", this.onLoad, this);
9412         }
9413         
9414         if(store){
9415             this.refresh();
9416         }
9417     },
9418     /**
9419      * onbeforeLoad - masks the loading area.
9420      *
9421      */
9422     onBeforeLoad : function(store,opts)
9423     {
9424          Roo.log('onBeforeLoad');   
9425         if (!opts.add) {
9426             this.el.update("");
9427         }
9428         this.el.mask(this.mask ? this.mask : "Loading" ); 
9429     },
9430     onLoad : function ()
9431     {
9432         this.el.unmask();
9433     },
9434     
9435
9436     /**
9437      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9438      * @param {HTMLElement} node
9439      * @return {HTMLElement} The template node
9440      */
9441     findItemFromChild : function(node){
9442         var el = this.dataName  ?
9443             this.el.child('.roo-tpl-' + this.dataName,true) :
9444             this.el.dom; 
9445         
9446         if(!node || node.parentNode == el){
9447                     return node;
9448             }
9449             var p = node.parentNode;
9450             while(p && p != el){
9451             if(p.parentNode == el){
9452                 return p;
9453             }
9454             p = p.parentNode;
9455         }
9456             return null;
9457     },
9458
9459     /** @ignore */
9460     onClick : function(e){
9461         var item = this.findItemFromChild(e.getTarget());
9462         if(item){
9463             var index = this.indexOf(item);
9464             if(this.onItemClick(item, index, e) !== false){
9465                 this.fireEvent("click", this, index, item, e);
9466             }
9467         }else{
9468             this.clearSelections();
9469         }
9470     },
9471
9472     /** @ignore */
9473     onContextMenu : function(e){
9474         var item = this.findItemFromChild(e.getTarget());
9475         if(item){
9476             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9477         }
9478     },
9479
9480     /** @ignore */
9481     onDblClick : function(e){
9482         var item = this.findItemFromChild(e.getTarget());
9483         if(item){
9484             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9485         }
9486     },
9487
9488     onItemClick : function(item, index, e)
9489     {
9490         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9491             return false;
9492         }
9493         if (this.toggleSelect) {
9494             var m = this.isSelected(item) ? 'unselect' : 'select';
9495             Roo.log(m);
9496             var _t = this;
9497             _t[m](item, true, false);
9498             return true;
9499         }
9500         if(this.multiSelect || this.singleSelect){
9501             if(this.multiSelect && e.shiftKey && this.lastSelection){
9502                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9503             }else{
9504                 this.select(item, this.multiSelect && e.ctrlKey);
9505                 this.lastSelection = item;
9506             }
9507             e.preventDefault();
9508         }
9509         return true;
9510     },
9511
9512     /**
9513      * Get the number of selected nodes.
9514      * @return {Number}
9515      */
9516     getSelectionCount : function(){
9517         return this.selections.length;
9518     },
9519
9520     /**
9521      * Get the currently selected nodes.
9522      * @return {Array} An array of HTMLElements
9523      */
9524     getSelectedNodes : function(){
9525         return this.selections;
9526     },
9527
9528     /**
9529      * Get the indexes of the selected nodes.
9530      * @return {Array}
9531      */
9532     getSelectedIndexes : function(){
9533         var indexes = [], s = this.selections;
9534         for(var i = 0, len = s.length; i < len; i++){
9535             indexes.push(s[i].nodeIndex);
9536         }
9537         return indexes;
9538     },
9539
9540     /**
9541      * Clear all selections
9542      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9543      */
9544     clearSelections : function(suppressEvent){
9545         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9546             this.cmp.elements = this.selections;
9547             this.cmp.removeClass(this.selectedClass);
9548             this.selections = [];
9549             if(!suppressEvent){
9550                 this.fireEvent("selectionchange", this, this.selections);
9551             }
9552         }
9553     },
9554
9555     /**
9556      * Returns true if the passed node is selected
9557      * @param {HTMLElement/Number} node The node or node index
9558      * @return {Boolean}
9559      */
9560     isSelected : function(node){
9561         var s = this.selections;
9562         if(s.length < 1){
9563             return false;
9564         }
9565         node = this.getNode(node);
9566         return s.indexOf(node) !== -1;
9567     },
9568
9569     /**
9570      * Selects nodes.
9571      * @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
9572      * @param {Boolean} keepExisting (optional) true to keep existing selections
9573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9574      */
9575     select : function(nodeInfo, keepExisting, suppressEvent){
9576         if(nodeInfo instanceof Array){
9577             if(!keepExisting){
9578                 this.clearSelections(true);
9579             }
9580             for(var i = 0, len = nodeInfo.length; i < len; i++){
9581                 this.select(nodeInfo[i], true, true);
9582             }
9583             return;
9584         } 
9585         var node = this.getNode(nodeInfo);
9586         if(!node || this.isSelected(node)){
9587             return; // already selected.
9588         }
9589         if(!keepExisting){
9590             this.clearSelections(true);
9591         }
9592         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9593             Roo.fly(node).addClass(this.selectedClass);
9594             this.selections.push(node);
9595             if(!suppressEvent){
9596                 this.fireEvent("selectionchange", this, this.selections);
9597             }
9598         }
9599         
9600         
9601     },
9602       /**
9603      * Unselects nodes.
9604      * @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
9605      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9606      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9607      */
9608     unselect : function(nodeInfo, keepExisting, suppressEvent)
9609     {
9610         if(nodeInfo instanceof Array){
9611             Roo.each(this.selections, function(s) {
9612                 this.unselect(s, nodeInfo);
9613             }, this);
9614             return;
9615         }
9616         var node = this.getNode(nodeInfo);
9617         if(!node || !this.isSelected(node)){
9618             Roo.log("not selected");
9619             return; // not selected.
9620         }
9621         // fireevent???
9622         var ns = [];
9623         Roo.each(this.selections, function(s) {
9624             if (s == node ) {
9625                 Roo.fly(node).removeClass(this.selectedClass);
9626
9627                 return;
9628             }
9629             ns.push(s);
9630         },this);
9631         
9632         this.selections= ns;
9633         this.fireEvent("selectionchange", this, this.selections);
9634     },
9635
9636     /**
9637      * Gets a template node.
9638      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9639      * @return {HTMLElement} The node or null if it wasn't found
9640      */
9641     getNode : function(nodeInfo){
9642         if(typeof nodeInfo == "string"){
9643             return document.getElementById(nodeInfo);
9644         }else if(typeof nodeInfo == "number"){
9645             return this.nodes[nodeInfo];
9646         }
9647         return nodeInfo;
9648     },
9649
9650     /**
9651      * Gets a range template nodes.
9652      * @param {Number} startIndex
9653      * @param {Number} endIndex
9654      * @return {Array} An array of nodes
9655      */
9656     getNodes : function(start, end){
9657         var ns = this.nodes;
9658         start = start || 0;
9659         end = typeof end == "undefined" ? ns.length - 1 : end;
9660         var nodes = [];
9661         if(start <= end){
9662             for(var i = start; i <= end; i++){
9663                 nodes.push(ns[i]);
9664             }
9665         } else{
9666             for(var i = start; i >= end; i--){
9667                 nodes.push(ns[i]);
9668             }
9669         }
9670         return nodes;
9671     },
9672
9673     /**
9674      * Finds the index of the passed node
9675      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9676      * @return {Number} The index of the node or -1
9677      */
9678     indexOf : function(node){
9679         node = this.getNode(node);
9680         if(typeof node.nodeIndex == "number"){
9681             return node.nodeIndex;
9682         }
9683         var ns = this.nodes;
9684         for(var i = 0, len = ns.length; i < len; i++){
9685             if(ns[i] == node){
9686                 return i;
9687             }
9688         }
9689         return -1;
9690     }
9691 });
9692 /*
9693  * - LGPL
9694  *
9695  * based on jquery fullcalendar
9696  * 
9697  */
9698
9699 Roo.bootstrap = Roo.bootstrap || {};
9700 /**
9701  * @class Roo.bootstrap.Calendar
9702  * @extends Roo.bootstrap.Component
9703  * Bootstrap Calendar class
9704  * @cfg {Boolean} loadMask (true|false) default false
9705     
9706  * @constructor
9707  * Create a new Container
9708  * @param {Object} config The config object
9709  */
9710
9711
9712
9713 Roo.bootstrap.Calendar = function(config){
9714     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9715      this.addEvents({
9716         /**
9717              * @event select
9718              * Fires when a date is selected
9719              * @param {DatePicker} this
9720              * @param {Date} date The selected date
9721              */
9722         'select': true,
9723         /**
9724              * @event monthchange
9725              * Fires when the displayed month changes 
9726              * @param {DatePicker} this
9727              * @param {Date} date The selected month
9728              */
9729         'monthchange': true,
9730         /**
9731              * @event evententer
9732              * Fires when mouse over an event
9733              * @param {Calendar} this
9734              * @param {event} Event
9735              */
9736         'evententer': true,
9737         /**
9738              * @event eventleave
9739              * Fires when the mouse leaves an
9740              * @param {Calendar} this
9741              * @param {event}
9742              */
9743         'eventleave': true,
9744         /**
9745              * @event eventclick
9746              * Fires when the mouse click an
9747              * @param {Calendar} this
9748              * @param {event}
9749              */
9750         'eventclick': true
9751         
9752     });
9753
9754 };
9755
9756 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9757     
9758      /**
9759      * @cfg {Number} startDay
9760      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9761      */
9762     startDay : 0,
9763     
9764     loadMask : false,
9765       
9766     getAutoCreate : function(){
9767         
9768         
9769         var fc_button = function(name, corner, style, content ) {
9770             return Roo.apply({},{
9771                 tag : 'span',
9772                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9773                          (corner.length ?
9774                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9775                             ''
9776                         ),
9777                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9778                 unselectable: 'on'
9779             });
9780         };
9781         
9782         var header = {
9783             tag : 'table',
9784             cls : 'fc-header',
9785             style : 'width:100%',
9786             cn : [
9787                 {
9788                     tag: 'tr',
9789                     cn : [
9790                         {
9791                             tag : 'td',
9792                             cls : 'fc-header-left',
9793                             cn : [
9794                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9795                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9796                                 { tag: 'span', cls: 'fc-header-space' },
9797                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9798                                 
9799                                 
9800                             ]
9801                         },
9802                         
9803                         {
9804                             tag : 'td',
9805                             cls : 'fc-header-center',
9806                             cn : [
9807                                 {
9808                                     tag: 'span',
9809                                     cls: 'fc-header-title',
9810                                     cn : {
9811                                         tag: 'H2',
9812                                         html : 'month / year'
9813                                     }
9814                                 }
9815                                 
9816                             ]
9817                         },
9818                         {
9819                             tag : 'td',
9820                             cls : 'fc-header-right',
9821                             cn : [
9822                           /*      fc_button('month', 'left', '', 'month' ),
9823                                 fc_button('week', '', '', 'week' ),
9824                                 fc_button('day', 'right', '', 'day' )
9825                             */    
9826                                 
9827                             ]
9828                         }
9829                         
9830                     ]
9831                 }
9832             ]
9833         };
9834         
9835        
9836         var cal_heads = function() {
9837             var ret = [];
9838             // fixme - handle this.
9839             
9840             for (var i =0; i < Date.dayNames.length; i++) {
9841                 var d = Date.dayNames[i];
9842                 ret.push({
9843                     tag: 'th',
9844                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9845                     html : d.substring(0,3)
9846                 });
9847                 
9848             }
9849             ret[0].cls += ' fc-first';
9850             ret[6].cls += ' fc-last';
9851             return ret;
9852         };
9853         var cal_cell = function(n) {
9854             return  {
9855                 tag: 'td',
9856                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9857                 cn : [
9858                     {
9859                         cn : [
9860                             {
9861                                 cls: 'fc-day-number',
9862                                 html: 'D'
9863                             },
9864                             {
9865                                 cls: 'fc-day-content',
9866                              
9867                                 cn : [
9868                                      {
9869                                         style: 'position: relative;' // height: 17px;
9870                                     }
9871                                 ]
9872                             }
9873                             
9874                             
9875                         ]
9876                     }
9877                 ]
9878                 
9879             }
9880         };
9881         var cal_rows = function() {
9882             
9883             var ret = []
9884             for (var r = 0; r < 6; r++) {
9885                 var row= {
9886                     tag : 'tr',
9887                     cls : 'fc-week',
9888                     cn : []
9889                 };
9890                 
9891                 for (var i =0; i < Date.dayNames.length; i++) {
9892                     var d = Date.dayNames[i];
9893                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9894
9895                 }
9896                 row.cn[0].cls+=' fc-first';
9897                 row.cn[0].cn[0].style = 'min-height:90px';
9898                 row.cn[6].cls+=' fc-last';
9899                 ret.push(row);
9900                 
9901             }
9902             ret[0].cls += ' fc-first';
9903             ret[4].cls += ' fc-prev-last';
9904             ret[5].cls += ' fc-last';
9905             return ret;
9906             
9907         };
9908         
9909         var cal_table = {
9910             tag: 'table',
9911             cls: 'fc-border-separate',
9912             style : 'width:100%',
9913             cellspacing  : 0,
9914             cn : [
9915                 { 
9916                     tag: 'thead',
9917                     cn : [
9918                         { 
9919                             tag: 'tr',
9920                             cls : 'fc-first fc-last',
9921                             cn : cal_heads()
9922                         }
9923                     ]
9924                 },
9925                 { 
9926                     tag: 'tbody',
9927                     cn : cal_rows()
9928                 }
9929                   
9930             ]
9931         };
9932          
9933          var cfg = {
9934             cls : 'fc fc-ltr',
9935             cn : [
9936                 header,
9937                 {
9938                     cls : 'fc-content',
9939                     style : "position: relative;",
9940                     cn : [
9941                         {
9942                             cls : 'fc-view fc-view-month fc-grid',
9943                             style : 'position: relative',
9944                             unselectable : 'on',
9945                             cn : [
9946                                 {
9947                                     cls : 'fc-event-container',
9948                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9949                                 },
9950                                 cal_table
9951                             ]
9952                         }
9953                     ]
9954     
9955                 }
9956            ] 
9957             
9958         };
9959         
9960          
9961         
9962         return cfg;
9963     },
9964     
9965     
9966     initEvents : function()
9967     {
9968         if(!this.store){
9969             throw "can not find store for calendar";
9970         }
9971         
9972         var mark = {
9973             tag: "div",
9974             cls:"x-dlg-mask",
9975             style: "text-align:center",
9976             cn: [
9977                 {
9978                     tag: "div",
9979                     style: "background-color:white;width:50%;margin:250 auto",
9980                     cn: [
9981                         {
9982                             tag: "img",
9983                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9984                         },
9985                         {
9986                             tag: "span",
9987                             html: "Loading"
9988                         }
9989                         
9990                     ]
9991                 }
9992             ]
9993         }
9994         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9995         
9996         var size = this.el.select('.fc-content', true).first().getSize();
9997         this.maskEl.setSize(size.width, size.height);
9998         this.maskEl.enableDisplayMode("block");
9999         if(!this.loadMask){
10000             this.maskEl.hide();
10001         }
10002         
10003         this.store = Roo.factory(this.store, Roo.data);
10004         this.store.on('load', this.onLoad, this);
10005         this.store.on('beforeload', this.onBeforeLoad, this);
10006         
10007         this.resize();
10008         
10009         this.cells = this.el.select('.fc-day',true);
10010         //Roo.log(this.cells);
10011         this.textNodes = this.el.query('.fc-day-number');
10012         this.cells.addClassOnOver('fc-state-hover');
10013         
10014         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10015         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10016         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10017         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10018         
10019         this.on('monthchange', this.onMonthChange, this);
10020         
10021         this.update(new Date().clearTime());
10022     },
10023     
10024     resize : function() {
10025         var sz  = this.el.getSize();
10026         
10027         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10028         this.el.select('.fc-day-content div',true).setHeight(34);
10029     },
10030     
10031     
10032     // private
10033     showPrevMonth : function(e){
10034         this.update(this.activeDate.add("mo", -1));
10035     },
10036     showToday : function(e){
10037         this.update(new Date().clearTime());
10038     },
10039     // private
10040     showNextMonth : function(e){
10041         this.update(this.activeDate.add("mo", 1));
10042     },
10043
10044     // private
10045     showPrevYear : function(){
10046         this.update(this.activeDate.add("y", -1));
10047     },
10048
10049     // private
10050     showNextYear : function(){
10051         this.update(this.activeDate.add("y", 1));
10052     },
10053
10054     
10055    // private
10056     update : function(date)
10057     {
10058         var vd = this.activeDate;
10059         this.activeDate = date;
10060 //        if(vd && this.el){
10061 //            var t = date.getTime();
10062 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10063 //                Roo.log('using add remove');
10064 //                
10065 //                this.fireEvent('monthchange', this, date);
10066 //                
10067 //                this.cells.removeClass("fc-state-highlight");
10068 //                this.cells.each(function(c){
10069 //                   if(c.dateValue == t){
10070 //                       c.addClass("fc-state-highlight");
10071 //                       setTimeout(function(){
10072 //                            try{c.dom.firstChild.focus();}catch(e){}
10073 //                       }, 50);
10074 //                       return false;
10075 //                   }
10076 //                   return true;
10077 //                });
10078 //                return;
10079 //            }
10080 //        }
10081         
10082         var days = date.getDaysInMonth();
10083         
10084         var firstOfMonth = date.getFirstDateOfMonth();
10085         var startingPos = firstOfMonth.getDay()-this.startDay;
10086         
10087         if(startingPos < this.startDay){
10088             startingPos += 7;
10089         }
10090         
10091         var pm = date.add(Date.MONTH, -1);
10092         var prevStart = pm.getDaysInMonth()-startingPos;
10093 //        
10094         this.cells = this.el.select('.fc-day',true);
10095         this.textNodes = this.el.query('.fc-day-number');
10096         this.cells.addClassOnOver('fc-state-hover');
10097         
10098         var cells = this.cells.elements;
10099         var textEls = this.textNodes;
10100         
10101         Roo.each(cells, function(cell){
10102             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10103         });
10104         
10105         days += startingPos;
10106
10107         // convert everything to numbers so it's fast
10108         var day = 86400000;
10109         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10110         //Roo.log(d);
10111         //Roo.log(pm);
10112         //Roo.log(prevStart);
10113         
10114         var today = new Date().clearTime().getTime();
10115         var sel = date.clearTime().getTime();
10116         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10117         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10118         var ddMatch = this.disabledDatesRE;
10119         var ddText = this.disabledDatesText;
10120         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10121         var ddaysText = this.disabledDaysText;
10122         var format = this.format;
10123         
10124         var setCellClass = function(cal, cell){
10125             
10126             //Roo.log('set Cell Class');
10127             cell.title = "";
10128             var t = d.getTime();
10129             
10130             //Roo.log(d);
10131             
10132             cell.dateValue = t;
10133             if(t == today){
10134                 cell.className += " fc-today";
10135                 cell.className += " fc-state-highlight";
10136                 cell.title = cal.todayText;
10137             }
10138             if(t == sel){
10139                 // disable highlight in other month..
10140                 //cell.className += " fc-state-highlight";
10141                 
10142             }
10143             // disabling
10144             if(t < min) {
10145                 cell.className = " fc-state-disabled";
10146                 cell.title = cal.minText;
10147                 return;
10148             }
10149             if(t > max) {
10150                 cell.className = " fc-state-disabled";
10151                 cell.title = cal.maxText;
10152                 return;
10153             }
10154             if(ddays){
10155                 if(ddays.indexOf(d.getDay()) != -1){
10156                     cell.title = ddaysText;
10157                     cell.className = " fc-state-disabled";
10158                 }
10159             }
10160             if(ddMatch && format){
10161                 var fvalue = d.dateFormat(format);
10162                 if(ddMatch.test(fvalue)){
10163                     cell.title = ddText.replace("%0", fvalue);
10164                     cell.className = " fc-state-disabled";
10165                 }
10166             }
10167             
10168             if (!cell.initialClassName) {
10169                 cell.initialClassName = cell.dom.className;
10170             }
10171             
10172             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10173         };
10174
10175         var i = 0;
10176         
10177         for(; i < startingPos; i++) {
10178             textEls[i].innerHTML = (++prevStart);
10179             d.setDate(d.getDate()+1);
10180             
10181             cells[i].className = "fc-past fc-other-month";
10182             setCellClass(this, cells[i]);
10183         }
10184         
10185         var intDay = 0;
10186         
10187         for(; i < days; i++){
10188             intDay = i - startingPos + 1;
10189             textEls[i].innerHTML = (intDay);
10190             d.setDate(d.getDate()+1);
10191             
10192             cells[i].className = ''; // "x-date-active";
10193             setCellClass(this, cells[i]);
10194         }
10195         var extraDays = 0;
10196         
10197         for(; i < 42; i++) {
10198             textEls[i].innerHTML = (++extraDays);
10199             d.setDate(d.getDate()+1);
10200             
10201             cells[i].className = "fc-future fc-other-month";
10202             setCellClass(this, cells[i]);
10203         }
10204         
10205         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10206         
10207         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10208         
10209         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10210         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10211         
10212         if(totalRows != 6){
10213             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10214             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10215         }
10216         
10217         this.fireEvent('monthchange', this, date);
10218         
10219         
10220         /*
10221         if(!this.internalRender){
10222             var main = this.el.dom.firstChild;
10223             var w = main.offsetWidth;
10224             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10225             Roo.fly(main).setWidth(w);
10226             this.internalRender = true;
10227             // opera does not respect the auto grow header center column
10228             // then, after it gets a width opera refuses to recalculate
10229             // without a second pass
10230             if(Roo.isOpera && !this.secondPass){
10231                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10232                 this.secondPass = true;
10233                 this.update.defer(10, this, [date]);
10234             }
10235         }
10236         */
10237         
10238     },
10239     
10240     findCell : function(dt) {
10241         dt = dt.clearTime().getTime();
10242         var ret = false;
10243         this.cells.each(function(c){
10244             //Roo.log("check " +c.dateValue + '?=' + dt);
10245             if(c.dateValue == dt){
10246                 ret = c;
10247                 return false;
10248             }
10249             return true;
10250         });
10251         
10252         return ret;
10253     },
10254     
10255     findCells : function(ev) {
10256         var s = ev.start.clone().clearTime().getTime();
10257        // Roo.log(s);
10258         var e= ev.end.clone().clearTime().getTime();
10259        // Roo.log(e);
10260         var ret = [];
10261         this.cells.each(function(c){
10262              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10263             
10264             if(c.dateValue > e){
10265                 return ;
10266             }
10267             if(c.dateValue < s){
10268                 return ;
10269             }
10270             ret.push(c);
10271         });
10272         
10273         return ret;    
10274     },
10275     
10276     findBestRow: function(cells)
10277     {
10278         var ret = 0;
10279         
10280         for (var i =0 ; i < cells.length;i++) {
10281             ret  = Math.max(cells[i].rows || 0,ret);
10282         }
10283         return ret;
10284         
10285     },
10286     
10287     
10288     addItem : function(ev)
10289     {
10290         // look for vertical location slot in
10291         var cells = this.findCells(ev);
10292         
10293         ev.row = this.findBestRow(cells);
10294         
10295         // work out the location.
10296         
10297         var crow = false;
10298         var rows = [];
10299         for(var i =0; i < cells.length; i++) {
10300             if (!crow) {
10301                 crow = {
10302                     start : cells[i],
10303                     end :  cells[i]
10304                 };
10305                 continue;
10306             }
10307             if (crow.start.getY() == cells[i].getY()) {
10308                 // on same row.
10309                 crow.end = cells[i];
10310                 continue;
10311             }
10312             // different row.
10313             rows.push(crow);
10314             crow = {
10315                 start: cells[i],
10316                 end : cells[i]
10317             };
10318             
10319         }
10320         
10321         rows.push(crow);
10322         ev.els = [];
10323         ev.rows = rows;
10324         ev.cells = cells;
10325         for (var i = 0; i < cells.length;i++) {
10326             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10327             
10328         }
10329         
10330         this.calevents.push(ev);
10331     },
10332     
10333     clearEvents: function() {
10334         
10335         if(!this.calevents){
10336             return;
10337         }
10338         
10339         Roo.each(this.cells.elements, function(c){
10340             c.rows = 0;
10341         });
10342         
10343         Roo.each(this.calevents, function(e) {
10344             Roo.each(e.els, function(el) {
10345                 el.un('mouseenter' ,this.onEventEnter, this);
10346                 el.un('mouseleave' ,this.onEventLeave, this);
10347                 el.remove();
10348             },this);
10349         },this);
10350         
10351     },
10352     
10353     renderEvents: function()
10354     {   
10355         // first make sure there is enough space..
10356         
10357         this.cells.each(function(c) {
10358         
10359             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10360         });
10361         
10362         for (var e = 0; e < this.calevents.length; e++) {
10363             var ev = this.calevents[e];
10364             var cells = ev.cells;
10365             var rows = ev.rows;
10366             
10367             for(var i =0; i < rows.length; i++) {
10368                 
10369                  
10370                 // how many rows should it span..
10371                 
10372                 var  cfg = {
10373                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10374                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10375                     
10376                     unselectable : "on",
10377                     cn : [
10378                         {
10379                             cls: 'fc-event-inner',
10380                             cn : [
10381 //                                {
10382 //                                  tag:'span',
10383 //                                  cls: 'fc-event-time',
10384 //                                  html : cells.length > 1 ? '' : ev.time
10385 //                                },
10386                                 {
10387                                   tag:'span',
10388                                   cls: 'fc-event-title',
10389                                   html : String.format('{0}', ev.title)
10390                                 }
10391                                 
10392                                 
10393                             ]
10394                         },
10395                         {
10396                             cls: 'ui-resizable-handle ui-resizable-e',
10397                             html : '&nbsp;&nbsp;&nbsp'
10398                         }
10399                         
10400                     ]
10401                 };
10402                 if (i == 0) {
10403                     cfg.cls += ' fc-event-start';
10404                 }
10405                 if ((i+1) == rows.length) {
10406                     cfg.cls += ' fc-event-end';
10407                 }
10408                 
10409                 var ctr = this.el.select('.fc-event-container',true).first();
10410                 var cg = ctr.createChild(cfg);
10411                 
10412                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10413                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10414                 cg.on('click', this.onEventClick, this, ev);
10415                 
10416                 ev.els.push(cg);
10417                 
10418                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10419                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10420                 //Roo.log(cg);
10421                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10422                 cg.setWidth(ebox.right - sbox.x -2);
10423             }
10424             
10425             
10426         }
10427         
10428     },
10429     
10430     onEventEnter: function (e, el,event,d) {
10431         this.fireEvent('evententer', this, el, event);
10432     },
10433     
10434     onEventLeave: function (e, el,event,d) {
10435         this.fireEvent('eventleave', this, el, event);
10436     },
10437     
10438     onEventClick: function (e, el,event,d) {
10439         this.fireEvent('eventclick', this, el, event);
10440     },
10441     
10442     onMonthChange: function () {
10443         this.store.load();
10444     },
10445     
10446     onLoad: function () 
10447     {   
10448         this.calevents = [];
10449         var cal = this;
10450         
10451         if(this.store.getCount() > 0){
10452             this.store.data.each(function(d){
10453                cal.addItem({
10454                     id : d.data.id,
10455                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10456                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10457                     time : d.data.start_time,
10458                     title : d.data.title,
10459                     description : d.data.description,
10460                     venue : d.data.venue
10461                 });
10462             });
10463         }
10464         
10465         this.renderEvents();
10466         
10467         if(this.loadMask){
10468             this.maskEl.hide();
10469         }
10470     },
10471     
10472     onBeforeLoad: function()
10473     {
10474         this.clearEvents();
10475         
10476         if(this.loadMask){
10477             this.maskEl.show();
10478         }
10479     }
10480 });
10481
10482  
10483  /*
10484  * - LGPL
10485  *
10486  * element
10487  * 
10488  */
10489
10490 /**
10491  * @class Roo.bootstrap.Popover
10492  * @extends Roo.bootstrap.Component
10493  * Bootstrap Popover class
10494  * @cfg {String} html contents of the popover   (or false to use children..)
10495  * @cfg {String} title of popover (or false to hide)
10496  * @cfg {String} placement how it is placed
10497  * @cfg {String} trigger click || hover (or false to trigger manually)
10498  * @cfg {String} over what (parent or false to trigger manually.)
10499  * 
10500  * @constructor
10501  * Create a new Popover
10502  * @param {Object} config The config object
10503  */
10504
10505 Roo.bootstrap.Popover = function(config){
10506     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10507 };
10508
10509 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10510     
10511     title: 'Fill in a title',
10512     html: false,
10513     
10514     placement : 'right',
10515     trigger : 'hover', // hover
10516     
10517     over: 'parent',
10518     
10519     can_build_overlaid : false,
10520     
10521     getChildContainer : function()
10522     {
10523         return this.el.select('.popover-content',true).first();
10524     },
10525     
10526     getAutoCreate : function(){
10527          Roo.log('make popover?');
10528         var cfg = {
10529            cls : 'popover roo-dynamic',
10530            style: 'display:block',
10531            cn : [
10532                 {
10533                     cls : 'arrow'
10534                 },
10535                 {
10536                     cls : 'popover-inner',
10537                     cn : [
10538                         {
10539                             tag: 'h3',
10540                             cls: 'popover-title',
10541                             html : this.title
10542                         },
10543                         {
10544                             cls : 'popover-content',
10545                             html : this.html
10546                         }
10547                     ]
10548                     
10549                 }
10550            ]
10551         };
10552         
10553         return cfg;
10554     },
10555     setTitle: function(str)
10556     {
10557         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10558     },
10559     setContent: function(str)
10560     {
10561         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10562     },
10563     // as it get's added to the bottom of the page.
10564     onRender : function(ct, position)
10565     {
10566         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10567         if(!this.el){
10568             var cfg = Roo.apply({},  this.getAutoCreate());
10569             cfg.id = Roo.id();
10570             
10571             if (this.cls) {
10572                 cfg.cls += ' ' + this.cls;
10573             }
10574             if (this.style) {
10575                 cfg.style = this.style;
10576             }
10577             Roo.log("adding to ")
10578             this.el = Roo.get(document.body).createChild(cfg, position);
10579             Roo.log(this.el);
10580         }
10581         this.initEvents();
10582     },
10583     
10584     initEvents : function()
10585     {
10586         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10587         this.el.enableDisplayMode('block');
10588         this.el.hide();
10589         if (this.over === false) {
10590             return; 
10591         }
10592         if (this.triggers === false) {
10593             return;
10594         }
10595         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10596         var triggers = this.trigger ? this.trigger.split(' ') : [];
10597         Roo.each(triggers, function(trigger) {
10598         
10599             if (trigger == 'click') {
10600                 on_el.on('click', this.toggle, this);
10601             } else if (trigger != 'manual') {
10602                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10603                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10604       
10605                 on_el.on(eventIn  ,this.enter, this);
10606                 on_el.on(eventOut, this.leave, this);
10607             }
10608         }, this);
10609         
10610     },
10611     
10612     
10613     // private
10614     timeout : null,
10615     hoverState : null,
10616     
10617     toggle : function () {
10618         this.hoverState == 'in' ? this.leave() : this.enter();
10619     },
10620     
10621     enter : function () {
10622        
10623     
10624         clearTimeout(this.timeout);
10625     
10626         this.hoverState = 'in'
10627     
10628         if (!this.delay || !this.delay.show) {
10629             this.show();
10630             return 
10631         }
10632         var _t = this;
10633         this.timeout = setTimeout(function () {
10634             if (_t.hoverState == 'in') {
10635                 _t.show();
10636             }
10637         }, this.delay.show)
10638     },
10639     leave : function() {
10640         clearTimeout(this.timeout);
10641     
10642         this.hoverState = 'out'
10643     
10644         if (!this.delay || !this.delay.hide) {
10645             this.hide();
10646             return 
10647         }
10648         var _t = this;
10649         this.timeout = setTimeout(function () {
10650             if (_t.hoverState == 'out') {
10651                 _t.hide();
10652             }
10653         }, this.delay.hide)
10654     },
10655     
10656     show : function (on_el)
10657     {
10658         if (!on_el) {
10659             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10660         }
10661         // set content.
10662         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10663         if (this.html !== false) {
10664             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10665         }
10666         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10667         if (!this.title.length) {
10668             this.el.select('.popover-title',true).hide();
10669         }
10670         
10671         var placement = typeof this.placement == 'function' ?
10672             this.placement.call(this, this.el, on_el) :
10673             this.placement;
10674             
10675         var autoToken = /\s?auto?\s?/i;
10676         var autoPlace = autoToken.test(placement);
10677         if (autoPlace) {
10678             placement = placement.replace(autoToken, '') || 'top';
10679         }
10680         
10681         //this.el.detach()
10682         //this.el.setXY([0,0]);
10683         this.el.show();
10684         this.el.dom.style.display='block';
10685         this.el.addClass(placement);
10686         
10687         //this.el.appendTo(on_el);
10688         
10689         var p = this.getPosition();
10690         var box = this.el.getBox();
10691         
10692         if (autoPlace) {
10693             // fixme..
10694         }
10695         var align = Roo.bootstrap.Popover.alignment[placement]
10696         this.el.alignTo(on_el, align[0],align[1]);
10697         //var arrow = this.el.select('.arrow',true).first();
10698         //arrow.set(align[2], 
10699         
10700         this.el.addClass('in');
10701         this.hoverState = null;
10702         
10703         if (this.el.hasClass('fade')) {
10704             // fade it?
10705         }
10706         
10707     },
10708     hide : function()
10709     {
10710         this.el.setXY([0,0]);
10711         this.el.removeClass('in');
10712         this.el.hide();
10713         
10714     }
10715     
10716 });
10717
10718 Roo.bootstrap.Popover.alignment = {
10719     'left' : ['r-l', [-10,0], 'right'],
10720     'right' : ['l-r', [10,0], 'left'],
10721     'bottom' : ['t-b', [0,10], 'top'],
10722     'top' : [ 'b-t', [0,-10], 'bottom']
10723 };
10724
10725  /*
10726  * - LGPL
10727  *
10728  * Progress
10729  * 
10730  */
10731
10732 /**
10733  * @class Roo.bootstrap.Progress
10734  * @extends Roo.bootstrap.Component
10735  * Bootstrap Progress class
10736  * @cfg {Boolean} striped striped of the progress bar
10737  * @cfg {Boolean} active animated of the progress bar
10738  * 
10739  * 
10740  * @constructor
10741  * Create a new Progress
10742  * @param {Object} config The config object
10743  */
10744
10745 Roo.bootstrap.Progress = function(config){
10746     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10747 };
10748
10749 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10750     
10751     striped : false,
10752     active: false,
10753     
10754     getAutoCreate : function(){
10755         var cfg = {
10756             tag: 'div',
10757             cls: 'progress'
10758         };
10759         
10760         
10761         if(this.striped){
10762             cfg.cls += ' progress-striped';
10763         }
10764       
10765         if(this.active){
10766             cfg.cls += ' active';
10767         }
10768         
10769         
10770         return cfg;
10771     }
10772    
10773 });
10774
10775  
10776
10777  /*
10778  * - LGPL
10779  *
10780  * ProgressBar
10781  * 
10782  */
10783
10784 /**
10785  * @class Roo.bootstrap.ProgressBar
10786  * @extends Roo.bootstrap.Component
10787  * Bootstrap ProgressBar class
10788  * @cfg {Number} aria_valuenow aria-value now
10789  * @cfg {Number} aria_valuemin aria-value min
10790  * @cfg {Number} aria_valuemax aria-value max
10791  * @cfg {String} label label for the progress bar
10792  * @cfg {String} panel (success | info | warning | danger )
10793  * @cfg {String} role role of the progress bar
10794  * @cfg {String} sr_only text
10795  * 
10796  * 
10797  * @constructor
10798  * Create a new ProgressBar
10799  * @param {Object} config The config object
10800  */
10801
10802 Roo.bootstrap.ProgressBar = function(config){
10803     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10804 };
10805
10806 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10807     
10808     aria_valuenow : 0,
10809     aria_valuemin : 0,
10810     aria_valuemax : 100,
10811     label : false,
10812     panel : false,
10813     role : false,
10814     sr_only: false,
10815     
10816     getAutoCreate : function()
10817     {
10818         
10819         var cfg = {
10820             tag: 'div',
10821             cls: 'progress-bar',
10822             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10823         };
10824         
10825         if(this.sr_only){
10826             cfg.cn = {
10827                 tag: 'span',
10828                 cls: 'sr-only',
10829                 html: this.sr_only
10830             }
10831         }
10832         
10833         if(this.role){
10834             cfg.role = this.role;
10835         }
10836         
10837         if(this.aria_valuenow){
10838             cfg['aria-valuenow'] = this.aria_valuenow;
10839         }
10840         
10841         if(this.aria_valuemin){
10842             cfg['aria-valuemin'] = this.aria_valuemin;
10843         }
10844         
10845         if(this.aria_valuemax){
10846             cfg['aria-valuemax'] = this.aria_valuemax;
10847         }
10848         
10849         if(this.label && !this.sr_only){
10850             cfg.html = this.label;
10851         }
10852         
10853         if(this.panel){
10854             cfg.cls += ' progress-bar-' + this.panel;
10855         }
10856         
10857         return cfg;
10858     },
10859     
10860     update : function(aria_valuenow)
10861     {
10862         this.aria_valuenow = aria_valuenow;
10863         
10864         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10865     }
10866    
10867 });
10868
10869  
10870
10871  /*
10872  * - LGPL
10873  *
10874  * TabPanel
10875  * 
10876  */
10877
10878 /**
10879  * @class Roo.bootstrap.TabPanel
10880  * @extends Roo.bootstrap.Component
10881  * Bootstrap TabPanel class
10882  * @cfg {Boolean} active panel active
10883  * @cfg {String} html panel content
10884  * @cfg {String} tabId tab relate id
10885  * 
10886  * 
10887  * @constructor
10888  * Create a new TabPanel
10889  * @param {Object} config The config object
10890  */
10891
10892 Roo.bootstrap.TabPanel = function(config){
10893     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10894 };
10895
10896 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10897     
10898     active: false,
10899     html: false,
10900     tabId: false,
10901     
10902     getAutoCreate : function(){
10903         var cfg = {
10904             tag: 'div',
10905             cls: 'tab-pane',
10906             html: this.html || ''
10907         };
10908         
10909         if(this.active){
10910             cfg.cls += ' active';
10911         }
10912         
10913         if(this.tabId){
10914             cfg.tabId = this.tabId;
10915         }
10916         
10917         return cfg;
10918     }
10919    
10920 });
10921
10922  
10923
10924  /*
10925  * - LGPL
10926  *
10927  * DateField
10928  * 
10929  */
10930
10931 /**
10932  * @class Roo.bootstrap.DateField
10933  * @extends Roo.bootstrap.Input
10934  * Bootstrap DateField class
10935  * @cfg {Number} weekStart default 0
10936  * @cfg {Number} weekStart default 0
10937  * @cfg {Number} viewMode default empty, (months|years)
10938  * @cfg {Number} minViewMode default empty, (months|years)
10939  * @cfg {Number} startDate default -Infinity
10940  * @cfg {Number} endDate default Infinity
10941  * @cfg {Boolean} todayHighlight default false
10942  * @cfg {Boolean} todayBtn default false
10943  * @cfg {Boolean} calendarWeeks default false
10944  * @cfg {Object} daysOfWeekDisabled default empty
10945  * 
10946  * @cfg {Boolean} keyboardNavigation default true
10947  * @cfg {String} language default en
10948  * 
10949  * @constructor
10950  * Create a new DateField
10951  * @param {Object} config The config object
10952  */
10953
10954 Roo.bootstrap.DateField = function(config){
10955     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10956      this.addEvents({
10957             /**
10958              * @event show
10959              * Fires when this field show.
10960              * @param {Roo.bootstrap.DateField} this
10961              * @param {Mixed} date The date value
10962              */
10963             show : true,
10964             /**
10965              * @event show
10966              * Fires when this field hide.
10967              * @param {Roo.bootstrap.DateField} this
10968              * @param {Mixed} date The date value
10969              */
10970             hide : true,
10971             /**
10972              * @event select
10973              * Fires when select a date.
10974              * @param {Roo.bootstrap.DateField} this
10975              * @param {Mixed} date The date value
10976              */
10977             select : true
10978         });
10979 };
10980
10981 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10982     
10983     /**
10984      * @cfg {String} format
10985      * The default date format string which can be overriden for localization support.  The format must be
10986      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10987      */
10988     format : "m/d/y",
10989     /**
10990      * @cfg {String} altFormats
10991      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10992      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10993      */
10994     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10995     
10996     weekStart : 0,
10997     
10998     viewMode : '',
10999     
11000     minViewMode : '',
11001     
11002     todayHighlight : false,
11003     
11004     todayBtn: false,
11005     
11006     language: 'en',
11007     
11008     keyboardNavigation: true,
11009     
11010     calendarWeeks: false,
11011     
11012     startDate: -Infinity,
11013     
11014     endDate: Infinity,
11015     
11016     daysOfWeekDisabled: [],
11017     
11018     _events: [],
11019     
11020     UTCDate: function()
11021     {
11022         return new Date(Date.UTC.apply(Date, arguments));
11023     },
11024     
11025     UTCToday: function()
11026     {
11027         var today = new Date();
11028         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11029     },
11030     
11031     getDate: function() {
11032             var d = this.getUTCDate();
11033             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11034     },
11035     
11036     getUTCDate: function() {
11037             return this.date;
11038     },
11039     
11040     setDate: function(d) {
11041             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11042     },
11043     
11044     setUTCDate: function(d) {
11045             this.date = d;
11046             this.setValue(this.formatDate(this.date));
11047     },
11048         
11049     onRender: function(ct, position)
11050     {
11051         
11052         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11053         
11054         this.language = this.language || 'en';
11055         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11056         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11057         
11058         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11059         this.format = this.format || 'm/d/y';
11060         this.isInline = false;
11061         this.isInput = true;
11062         this.component = this.el.select('.add-on', true).first() || false;
11063         this.component = (this.component && this.component.length === 0) ? false : this.component;
11064         this.hasInput = this.component && this.inputEL().length;
11065         
11066         if (typeof(this.minViewMode === 'string')) {
11067             switch (this.minViewMode) {
11068                 case 'months':
11069                     this.minViewMode = 1;
11070                     break;
11071                 case 'years':
11072                     this.minViewMode = 2;
11073                     break;
11074                 default:
11075                     this.minViewMode = 0;
11076                     break;
11077             }
11078         }
11079         
11080         if (typeof(this.viewMode === 'string')) {
11081             switch (this.viewMode) {
11082                 case 'months':
11083                     this.viewMode = 1;
11084                     break;
11085                 case 'years':
11086                     this.viewMode = 2;
11087                     break;
11088                 default:
11089                     this.viewMode = 0;
11090                     break;
11091             }
11092         }
11093                 
11094         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11095         
11096         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11097         
11098         this.picker().on('mousedown', this.onMousedown, this);
11099         this.picker().on('click', this.onClick, this);
11100         
11101         this.picker().addClass('datepicker-dropdown');
11102         
11103         this.startViewMode = this.viewMode;
11104         
11105         
11106         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11107             if(!this.calendarWeeks){
11108                 v.remove();
11109                 return;
11110             };
11111             
11112             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11113             v.attr('colspan', function(i, val){
11114                 return parseInt(val) + 1;
11115             });
11116         })
11117                         
11118         
11119         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11120         
11121         this.setStartDate(this.startDate);
11122         this.setEndDate(this.endDate);
11123         
11124         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11125         
11126         this.fillDow();
11127         this.fillMonths();
11128         this.update();
11129         this.showMode();
11130         
11131         if(this.isInline) {
11132             this.show();
11133         }
11134     },
11135     
11136     picker : function()
11137     {
11138         return this.el.select('.datepicker', true).first();
11139     },
11140     
11141     fillDow: function()
11142     {
11143         var dowCnt = this.weekStart;
11144         
11145         var dow = {
11146             tag: 'tr',
11147             cn: [
11148                 
11149             ]
11150         };
11151         
11152         if(this.calendarWeeks){
11153             dow.cn.push({
11154                 tag: 'th',
11155                 cls: 'cw',
11156                 html: '&nbsp;'
11157             })
11158         }
11159         
11160         while (dowCnt < this.weekStart + 7) {
11161             dow.cn.push({
11162                 tag: 'th',
11163                 cls: 'dow',
11164                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11165             });
11166         }
11167         
11168         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11169     },
11170     
11171     fillMonths: function()
11172     {    
11173         var i = 0
11174         var months = this.picker().select('>.datepicker-months td', true).first();
11175         
11176         months.dom.innerHTML = '';
11177         
11178         while (i < 12) {
11179             var month = {
11180                 tag: 'span',
11181                 cls: 'month',
11182                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11183             }
11184             
11185             months.createChild(month);
11186         }
11187         
11188     },
11189     
11190     update: function(){
11191         
11192         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11193         
11194         if (this.date < this.startDate) {
11195             this.viewDate = new Date(this.startDate);
11196         } else if (this.date > this.endDate) {
11197             this.viewDate = new Date(this.endDate);
11198         } else {
11199             this.viewDate = new Date(this.date);
11200         }
11201         
11202         this.fill();
11203     },
11204     
11205     fill: function() {
11206         var d = new Date(this.viewDate),
11207                 year = d.getUTCFullYear(),
11208                 month = d.getUTCMonth(),
11209                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11210                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11211                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11212                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11213                 currentDate = this.date && this.date.valueOf(),
11214                 today = this.UTCToday();
11215         
11216         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11217         
11218 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11219         
11220 //        this.picker.select('>tfoot th.today').
11221 //                                              .text(dates[this.language].today)
11222 //                                              .toggle(this.todayBtn !== false);
11223     
11224         this.updateNavArrows();
11225         this.fillMonths();
11226                                                 
11227         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11228         
11229         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11230          
11231         prevMonth.setUTCDate(day);
11232         
11233         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11234         
11235         var nextMonth = new Date(prevMonth);
11236         
11237         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11238         
11239         nextMonth = nextMonth.valueOf();
11240         
11241         var fillMonths = false;
11242         
11243         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11244         
11245         while(prevMonth.valueOf() < nextMonth) {
11246             var clsName = '';
11247             
11248             if (prevMonth.getUTCDay() === this.weekStart) {
11249                 if(fillMonths){
11250                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11251                 }
11252                     
11253                 fillMonths = {
11254                     tag: 'tr',
11255                     cn: []
11256                 };
11257                 
11258                 if(this.calendarWeeks){
11259                     // ISO 8601: First week contains first thursday.
11260                     // ISO also states week starts on Monday, but we can be more abstract here.
11261                     var
11262                     // Start of current week: based on weekstart/current date
11263                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11264                     // Thursday of this week
11265                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11266                     // First Thursday of year, year from thursday
11267                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11268                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11269                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11270                     
11271                     fillMonths.cn.push({
11272                         tag: 'td',
11273                         cls: 'cw',
11274                         html: calWeek
11275                     });
11276                 }
11277             }
11278             
11279             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11280                 clsName += ' old';
11281             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11282                 clsName += ' new';
11283             }
11284             if (this.todayHighlight &&
11285                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11286                 prevMonth.getUTCMonth() == today.getMonth() &&
11287                 prevMonth.getUTCDate() == today.getDate()) {
11288                 clsName += ' today';
11289             }
11290             
11291             if (currentDate && prevMonth.valueOf() === currentDate) {
11292                 clsName += ' active';
11293             }
11294             
11295             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11296                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11297                     clsName += ' disabled';
11298             }
11299             
11300             fillMonths.cn.push({
11301                 tag: 'td',
11302                 cls: 'day ' + clsName,
11303                 html: prevMonth.getDate()
11304             })
11305             
11306             prevMonth.setDate(prevMonth.getDate()+1);
11307         }
11308           
11309         var currentYear = this.date && this.date.getUTCFullYear();
11310         var currentMonth = this.date && this.date.getUTCMonth();
11311         
11312         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11313         
11314         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11315             v.removeClass('active');
11316             
11317             if(currentYear === year && k === currentMonth){
11318                 v.addClass('active');
11319             }
11320             
11321             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11322                 v.addClass('disabled');
11323             }
11324             
11325         });
11326         
11327         
11328         year = parseInt(year/10, 10) * 10;
11329         
11330         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11331         
11332         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11333         
11334         year -= 1;
11335         for (var i = -1; i < 11; i++) {
11336             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11337                 tag: 'span',
11338                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11339                 html: year
11340             })
11341             
11342             year += 1;
11343         }
11344     },
11345     
11346     showMode: function(dir) {
11347         if (dir) {
11348             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11349         }
11350         Roo.each(this.picker().select('>div',true).elements, function(v){
11351             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11352             v.hide();
11353         });
11354         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11355     },
11356     
11357     place: function()
11358     {
11359         if(this.isInline) return;
11360         
11361         this.picker().removeClass(['bottom', 'top']);
11362         
11363         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11364             /*
11365              * place to the top of element!
11366              *
11367              */
11368             
11369             this.picker().addClass('top');
11370             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11371             
11372             return;
11373         }
11374         
11375         this.picker().addClass('bottom');
11376         
11377         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11378     },
11379     
11380     parseDate : function(value){
11381         if(!value || value instanceof Date){
11382             return value;
11383         }
11384         var v = Date.parseDate(value, this.format);
11385         if (!v && this.useIso) {
11386             v = Date.parseDate(value, 'Y-m-d');
11387         }
11388         if(!v && this.altFormats){
11389             if(!this.altFormatsArray){
11390                 this.altFormatsArray = this.altFormats.split("|");
11391             }
11392             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11393                 v = Date.parseDate(value, this.altFormatsArray[i]);
11394             }
11395         }
11396         return v;
11397     },
11398     
11399     formatDate : function(date, fmt){
11400         return (!date || !(date instanceof Date)) ?
11401         date : date.dateFormat(fmt || this.format);
11402     },
11403     
11404     onFocus : function()
11405     {
11406         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11407         this.show();
11408     },
11409     
11410     onBlur : function()
11411     {
11412         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11413         this.hide();
11414     },
11415     
11416     show : function()
11417     {
11418         this.picker().show();
11419         this.update();
11420         this.place();
11421         
11422         this.fireEvent('show', this, this.date);
11423     },
11424     
11425     hide : function()
11426     {
11427         if(this.isInline) return;
11428         this.picker().hide();
11429         this.viewMode = this.startViewMode;
11430         this.showMode();
11431         
11432         this.fireEvent('hide', this, this.date);
11433         
11434     },
11435     
11436     onMousedown: function(e){
11437         e.stopPropagation();
11438         e.preventDefault();
11439     },
11440     
11441     keyup: function(e){
11442         Roo.bootstrap.DateField.superclass.keyup.call(this);
11443         this.update();
11444         
11445     },
11446
11447     setValue: function(v){
11448         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11449         
11450         this.fireEvent('select', this, this.date);
11451         
11452     },
11453     
11454     fireKey: function(e){
11455         if (!this.picker().isVisible()){
11456             if (e.keyCode == 27) // allow escape to hide and re-show picker
11457                 this.show();
11458             return;
11459         }
11460         var dateChanged = false,
11461         dir, day, month,
11462         newDate, newViewDate;
11463         switch(e.keyCode){
11464             case 27: // escape
11465                 this.hide();
11466                 e.preventDefault();
11467                 break;
11468             case 37: // left
11469             case 39: // right
11470                 if (!this.keyboardNavigation) break;
11471                 dir = e.keyCode == 37 ? -1 : 1;
11472                 
11473                 if (e.ctrlKey){
11474                     newDate = this.moveYear(this.date, dir);
11475                     newViewDate = this.moveYear(this.viewDate, dir);
11476                 } else if (e.shiftKey){
11477                     newDate = this.moveMonth(this.date, dir);
11478                     newViewDate = this.moveMonth(this.viewDate, dir);
11479                 } else {
11480                     newDate = new Date(this.date);
11481                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11482                     newViewDate = new Date(this.viewDate);
11483                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11484                 }
11485                 if (this.dateWithinRange(newDate)){
11486                     this.date = newDate;
11487                     this.viewDate = newViewDate;
11488                     this.setValue(this.formatDate(this.date));
11489                     this.update();
11490                     e.preventDefault();
11491                     dateChanged = true;
11492                 }
11493                 break;
11494             case 38: // up
11495             case 40: // down
11496                 if (!this.keyboardNavigation) break;
11497                 dir = e.keyCode == 38 ? -1 : 1;
11498                 if (e.ctrlKey){
11499                     newDate = this.moveYear(this.date, dir);
11500                     newViewDate = this.moveYear(this.viewDate, dir);
11501                 } else if (e.shiftKey){
11502                     newDate = this.moveMonth(this.date, dir);
11503                     newViewDate = this.moveMonth(this.viewDate, dir);
11504                 } else {
11505                     newDate = new Date(this.date);
11506                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11507                     newViewDate = new Date(this.viewDate);
11508                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11509                 }
11510                 if (this.dateWithinRange(newDate)){
11511                     this.date = newDate;
11512                     this.viewDate = newViewDate;
11513                     this.setValue(this.formatDate(this.date));
11514                     this.update();
11515                     e.preventDefault();
11516                     dateChanged = true;
11517                 }
11518                 break;
11519             case 13: // enter
11520                 this.setValue(this.formatDate(this.date));
11521                 this.hide();
11522                 e.preventDefault();
11523                 break;
11524             case 9: // tab
11525                 this.setValue(this.formatDate(this.date));
11526                 this.hide();
11527                 break;
11528         }
11529     },
11530     
11531     
11532     onClick: function(e) {
11533         e.stopPropagation();
11534         e.preventDefault();
11535         
11536         var target = e.getTarget();
11537         
11538         if(target.nodeName.toLowerCase() === 'i'){
11539             target = Roo.get(target).dom.parentNode;
11540         }
11541         
11542         var nodeName = target.nodeName;
11543         var className = target.className;
11544         var html = target.innerHTML;
11545         
11546         switch(nodeName.toLowerCase()) {
11547             case 'th':
11548                 switch(className) {
11549                     case 'switch':
11550                         this.showMode(1);
11551                         break;
11552                     case 'prev':
11553                     case 'next':
11554                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11555                         switch(this.viewMode){
11556                                 case 0:
11557                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11558                                         break;
11559                                 case 1:
11560                                 case 2:
11561                                         this.viewDate = this.moveYear(this.viewDate, dir);
11562                                         break;
11563                         }
11564                         this.fill();
11565                         break;
11566                     case 'today':
11567                         var date = new Date();
11568                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11569                         this.fill()
11570                         this.setValue(this.formatDate(this.date));
11571                         this.hide();
11572                         break;
11573                 }
11574                 break;
11575             case 'span':
11576                 if (className.indexOf('disabled') === -1) {
11577                     this.viewDate.setUTCDate(1);
11578                     if (className.indexOf('month') !== -1) {
11579                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11580                     } else {
11581                         var year = parseInt(html, 10) || 0;
11582                         this.viewDate.setUTCFullYear(year);
11583                         
11584                     }
11585                     this.showMode(-1);
11586                     this.fill();
11587                 }
11588                 break;
11589                 
11590             case 'td':
11591                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11592                     var day = parseInt(html, 10) || 1;
11593                     var year = this.viewDate.getUTCFullYear(),
11594                         month = this.viewDate.getUTCMonth();
11595
11596                     if (className.indexOf('old') !== -1) {
11597                         if(month === 0 ){
11598                             month = 11;
11599                             year -= 1;
11600                         }else{
11601                             month -= 1;
11602                         }
11603                     } else if (className.indexOf('new') !== -1) {
11604                         if (month == 11) {
11605                             month = 0;
11606                             year += 1;
11607                         } else {
11608                             month += 1;
11609                         }
11610                     }
11611                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11612                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11613                     this.fill();
11614                     this.setValue(this.formatDate(this.date));
11615                     this.hide();
11616                 }
11617                 break;
11618         }
11619     },
11620     
11621     setStartDate: function(startDate){
11622         this.startDate = startDate || -Infinity;
11623         if (this.startDate !== -Infinity) {
11624             this.startDate = this.parseDate(this.startDate);
11625         }
11626         this.update();
11627         this.updateNavArrows();
11628     },
11629
11630     setEndDate: function(endDate){
11631         this.endDate = endDate || Infinity;
11632         if (this.endDate !== Infinity) {
11633             this.endDate = this.parseDate(this.endDate);
11634         }
11635         this.update();
11636         this.updateNavArrows();
11637     },
11638     
11639     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11640         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11641         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11642             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11643         }
11644         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11645             return parseInt(d, 10);
11646         });
11647         this.update();
11648         this.updateNavArrows();
11649     },
11650     
11651     updateNavArrows: function() {
11652         var d = new Date(this.viewDate),
11653         year = d.getUTCFullYear(),
11654         month = d.getUTCMonth();
11655         
11656         Roo.each(this.picker().select('.prev', true).elements, function(v){
11657             v.show();
11658             switch (this.viewMode) {
11659                 case 0:
11660
11661                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11662                         v.hide();
11663                     }
11664                     break;
11665                 case 1:
11666                 case 2:
11667                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11668                         v.hide();
11669                     }
11670                     break;
11671             }
11672         });
11673         
11674         Roo.each(this.picker().select('.next', true).elements, function(v){
11675             v.show();
11676             switch (this.viewMode) {
11677                 case 0:
11678
11679                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11680                         v.hide();
11681                     }
11682                     break;
11683                 case 1:
11684                 case 2:
11685                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11686                         v.hide();
11687                     }
11688                     break;
11689             }
11690         })
11691     },
11692     
11693     moveMonth: function(date, dir){
11694         if (!dir) return date;
11695         var new_date = new Date(date.valueOf()),
11696         day = new_date.getUTCDate(),
11697         month = new_date.getUTCMonth(),
11698         mag = Math.abs(dir),
11699         new_month, test;
11700         dir = dir > 0 ? 1 : -1;
11701         if (mag == 1){
11702             test = dir == -1
11703             // If going back one month, make sure month is not current month
11704             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11705             ? function(){
11706                 return new_date.getUTCMonth() == month;
11707             }
11708             // If going forward one month, make sure month is as expected
11709             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11710             : function(){
11711                 return new_date.getUTCMonth() != new_month;
11712             };
11713             new_month = month + dir;
11714             new_date.setUTCMonth(new_month);
11715             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11716             if (new_month < 0 || new_month > 11)
11717                 new_month = (new_month + 12) % 12;
11718         } else {
11719             // For magnitudes >1, move one month at a time...
11720             for (var i=0; i<mag; i++)
11721                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11722                 new_date = this.moveMonth(new_date, dir);
11723             // ...then reset the day, keeping it in the new month
11724             new_month = new_date.getUTCMonth();
11725             new_date.setUTCDate(day);
11726             test = function(){
11727                 return new_month != new_date.getUTCMonth();
11728             };
11729         }
11730         // Common date-resetting loop -- if date is beyond end of month, make it
11731         // end of month
11732         while (test()){
11733             new_date.setUTCDate(--day);
11734             new_date.setUTCMonth(new_month);
11735         }
11736         return new_date;
11737     },
11738
11739     moveYear: function(date, dir){
11740         return this.moveMonth(date, dir*12);
11741     },
11742
11743     dateWithinRange: function(date){
11744         return date >= this.startDate && date <= this.endDate;
11745     },
11746
11747     
11748     remove: function() {
11749         this.picker().remove();
11750     }
11751    
11752 });
11753
11754 Roo.apply(Roo.bootstrap.DateField,  {
11755     
11756     head : {
11757         tag: 'thead',
11758         cn: [
11759         {
11760             tag: 'tr',
11761             cn: [
11762             {
11763                 tag: 'th',
11764                 cls: 'prev',
11765                 html: '<i class="icon-arrow-left"/>'
11766             },
11767             {
11768                 tag: 'th',
11769                 cls: 'switch',
11770                 colspan: '5'
11771             },
11772             {
11773                 tag: 'th',
11774                 cls: 'next',
11775                 html: '<i class="icon-arrow-right"/>'
11776             }
11777
11778             ]
11779         }
11780         ]
11781     },
11782     
11783     content : {
11784         tag: 'tbody',
11785         cn: [
11786         {
11787             tag: 'tr',
11788             cn: [
11789             {
11790                 tag: 'td',
11791                 colspan: '7'
11792             }
11793             ]
11794         }
11795         ]
11796     },
11797     
11798     footer : {
11799         tag: 'tfoot',
11800         cn: [
11801         {
11802             tag: 'tr',
11803             cn: [
11804             {
11805                 tag: 'th',
11806                 colspan: '7',
11807                 cls: 'today'
11808             }
11809                     
11810             ]
11811         }
11812         ]
11813     },
11814     
11815     dates:{
11816         en: {
11817             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11818             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11819             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11820             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11821             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11822             today: "Today"
11823         }
11824     },
11825     
11826     modes: [
11827     {
11828         clsName: 'days',
11829         navFnc: 'Month',
11830         navStep: 1
11831     },
11832     {
11833         clsName: 'months',
11834         navFnc: 'FullYear',
11835         navStep: 1
11836     },
11837     {
11838         clsName: 'years',
11839         navFnc: 'FullYear',
11840         navStep: 10
11841     }]
11842 });
11843
11844 Roo.apply(Roo.bootstrap.DateField,  {
11845   
11846     template : {
11847         tag: 'div',
11848         cls: 'datepicker dropdown-menu',
11849         cn: [
11850         {
11851             tag: 'div',
11852             cls: 'datepicker-days',
11853             cn: [
11854             {
11855                 tag: 'table',
11856                 cls: 'table-condensed',
11857                 cn:[
11858                 Roo.bootstrap.DateField.head,
11859                 {
11860                     tag: 'tbody'
11861                 },
11862                 Roo.bootstrap.DateField.footer
11863                 ]
11864             }
11865             ]
11866         },
11867         {
11868             tag: 'div',
11869             cls: 'datepicker-months',
11870             cn: [
11871             {
11872                 tag: 'table',
11873                 cls: 'table-condensed',
11874                 cn:[
11875                 Roo.bootstrap.DateField.head,
11876                 Roo.bootstrap.DateField.content,
11877                 Roo.bootstrap.DateField.footer
11878                 ]
11879             }
11880             ]
11881         },
11882         {
11883             tag: 'div',
11884             cls: 'datepicker-years',
11885             cn: [
11886             {
11887                 tag: 'table',
11888                 cls: 'table-condensed',
11889                 cn:[
11890                 Roo.bootstrap.DateField.head,
11891                 Roo.bootstrap.DateField.content,
11892                 Roo.bootstrap.DateField.footer
11893                 ]
11894             }
11895             ]
11896         }
11897         ]
11898     }
11899 });
11900
11901  
11902
11903  /*
11904  * - LGPL
11905  *
11906  * TimeField
11907  * 
11908  */
11909
11910 /**
11911  * @class Roo.bootstrap.TimeField
11912  * @extends Roo.bootstrap.Input
11913  * Bootstrap DateField class
11914  * 
11915  * 
11916  * @constructor
11917  * Create a new TimeField
11918  * @param {Object} config The config object
11919  */
11920
11921 Roo.bootstrap.TimeField = function(config){
11922     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11923     this.addEvents({
11924             /**
11925              * @event show
11926              * Fires when this field show.
11927              * @param {Roo.bootstrap.DateField} this
11928              * @param {Mixed} date The date value
11929              */
11930             show : true,
11931             /**
11932              * @event show
11933              * Fires when this field hide.
11934              * @param {Roo.bootstrap.DateField} this
11935              * @param {Mixed} date The date value
11936              */
11937             hide : true,
11938             /**
11939              * @event select
11940              * Fires when select a date.
11941              * @param {Roo.bootstrap.DateField} this
11942              * @param {Mixed} date The date value
11943              */
11944             select : true
11945         });
11946 };
11947
11948 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11949     
11950     /**
11951      * @cfg {String} format
11952      * The default time format string which can be overriden for localization support.  The format must be
11953      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11954      */
11955     format : "H:i",
11956        
11957     onRender: function(ct, position)
11958     {
11959         
11960         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11961                 
11962         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11963         
11964         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11965         
11966         this.pop = this.picker().select('>.datepicker-time',true).first();
11967         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11968         
11969         this.picker().on('mousedown', this.onMousedown, this);
11970         this.picker().on('click', this.onClick, this);
11971         
11972         this.picker().addClass('datepicker-dropdown');
11973     
11974         this.fillTime();
11975         this.update();
11976             
11977         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11978         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11979         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11980         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11981         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11982         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11983
11984     },
11985     
11986     fireKey: function(e){
11987         if (!this.picker().isVisible()){
11988             if (e.keyCode == 27) // allow escape to hide and re-show picker
11989                 this.show();
11990             return;
11991         }
11992
11993         e.preventDefault();
11994         
11995         switch(e.keyCode){
11996             case 27: // escape
11997                 this.hide();
11998                 break;
11999             case 37: // left
12000             case 39: // right
12001                 this.onTogglePeriod();
12002                 break;
12003             case 38: // up
12004                 this.onIncrementMinutes();
12005                 break;
12006             case 40: // down
12007                 this.onDecrementMinutes();
12008                 break;
12009             case 13: // enter
12010             case 9: // tab
12011                 this.setTime();
12012                 break;
12013         }
12014     },
12015     
12016     onClick: function(e) {
12017         e.stopPropagation();
12018         e.preventDefault();
12019     },
12020     
12021     picker : function()
12022     {
12023         return this.el.select('.datepicker', true).first();
12024     },
12025     
12026     fillTime: function()
12027     {    
12028         var time = this.pop.select('tbody', true).first();
12029         
12030         time.dom.innerHTML = '';
12031         
12032         time.createChild({
12033             tag: 'tr',
12034             cn: [
12035                 {
12036                     tag: 'td',
12037                     cn: [
12038                         {
12039                             tag: 'a',
12040                             href: '#',
12041                             cls: 'btn',
12042                             cn: [
12043                                 {
12044                                     tag: 'span',
12045                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12046                                 }
12047                             ]
12048                         } 
12049                     ]
12050                 },
12051                 {
12052                     tag: 'td',
12053                     cls: 'separator'
12054                 },
12055                 {
12056                     tag: 'td',
12057                     cn: [
12058                         {
12059                             tag: 'a',
12060                             href: '#',
12061                             cls: 'btn',
12062                             cn: [
12063                                 {
12064                                     tag: 'span',
12065                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12066                                 }
12067                             ]
12068                         }
12069                     ]
12070                 },
12071                 {
12072                     tag: 'td',
12073                     cls: 'separator'
12074                 }
12075             ]
12076         });
12077         
12078         time.createChild({
12079             tag: 'tr',
12080             cn: [
12081                 {
12082                     tag: 'td',
12083                     cn: [
12084                         {
12085                             tag: 'span',
12086                             cls: 'timepicker-hour',
12087                             html: '00'
12088                         }  
12089                     ]
12090                 },
12091                 {
12092                     tag: 'td',
12093                     cls: 'separator',
12094                     html: ':'
12095                 },
12096                 {
12097                     tag: 'td',
12098                     cn: [
12099                         {
12100                             tag: 'span',
12101                             cls: 'timepicker-minute',
12102                             html: '00'
12103                         }  
12104                     ]
12105                 },
12106                 {
12107                     tag: 'td',
12108                     cls: 'separator'
12109                 },
12110                 {
12111                     tag: 'td',
12112                     cn: [
12113                         {
12114                             tag: 'button',
12115                             type: 'button',
12116                             cls: 'btn btn-primary period',
12117                             html: 'AM'
12118                             
12119                         }
12120                     ]
12121                 }
12122             ]
12123         });
12124         
12125         time.createChild({
12126             tag: 'tr',
12127             cn: [
12128                 {
12129                     tag: 'td',
12130                     cn: [
12131                         {
12132                             tag: 'a',
12133                             href: '#',
12134                             cls: 'btn',
12135                             cn: [
12136                                 {
12137                                     tag: 'span',
12138                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12139                                 }
12140                             ]
12141                         }
12142                     ]
12143                 },
12144                 {
12145                     tag: 'td',
12146                     cls: 'separator'
12147                 },
12148                 {
12149                     tag: 'td',
12150                     cn: [
12151                         {
12152                             tag: 'a',
12153                             href: '#',
12154                             cls: 'btn',
12155                             cn: [
12156                                 {
12157                                     tag: 'span',
12158                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12159                                 }
12160                             ]
12161                         }
12162                     ]
12163                 },
12164                 {
12165                     tag: 'td',
12166                     cls: 'separator'
12167                 }
12168             ]
12169         });
12170         
12171     },
12172     
12173     update: function()
12174     {
12175         
12176         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12177         
12178         this.fill();
12179     },
12180     
12181     fill: function() 
12182     {
12183         var hours = this.time.getHours();
12184         var minutes = this.time.getMinutes();
12185         var period = 'AM';
12186         
12187         if(hours > 11){
12188             period = 'PM';
12189         }
12190         
12191         if(hours == 0){
12192             hours = 12;
12193         }
12194         
12195         
12196         if(hours > 12){
12197             hours = hours - 12;
12198         }
12199         
12200         if(hours < 10){
12201             hours = '0' + hours;
12202         }
12203         
12204         if(minutes < 10){
12205             minutes = '0' + minutes;
12206         }
12207         
12208         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12209         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12210         this.pop.select('button', true).first().dom.innerHTML = period;
12211         
12212     },
12213     
12214     place: function()
12215     {   
12216         this.picker().removeClass(['bottom', 'top']);
12217         
12218         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12219             /*
12220              * place to the top of element!
12221              *
12222              */
12223             
12224             this.picker().addClass('top');
12225             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12226             
12227             return;
12228         }
12229         
12230         this.picker().addClass('bottom');
12231         
12232         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12233     },
12234   
12235     onFocus : function()
12236     {
12237         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12238         this.show();
12239     },
12240     
12241     onBlur : function()
12242     {
12243         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12244         this.hide();
12245     },
12246     
12247     show : function()
12248     {
12249         this.picker().show();
12250         this.pop.show();
12251         this.update();
12252         this.place();
12253         
12254         this.fireEvent('show', this, this.date);
12255     },
12256     
12257     hide : function()
12258     {
12259         this.picker().hide();
12260         this.pop.hide();
12261         
12262         this.fireEvent('hide', this, this.date);
12263     },
12264     
12265     setTime : function()
12266     {
12267         this.hide();
12268         this.setValue(this.time.format(this.format));
12269         
12270         this.fireEvent('select', this, this.date);
12271         
12272         
12273     },
12274     
12275     onMousedown: function(e){
12276         e.stopPropagation();
12277         e.preventDefault();
12278     },
12279     
12280     onIncrementHours: function()
12281     {
12282         Roo.log('onIncrementHours');
12283         this.time = this.time.add(Date.HOUR, 1);
12284         this.update();
12285         
12286     },
12287     
12288     onDecrementHours: function()
12289     {
12290         Roo.log('onDecrementHours');
12291         this.time = this.time.add(Date.HOUR, -1);
12292         this.update();
12293     },
12294     
12295     onIncrementMinutes: function()
12296     {
12297         Roo.log('onIncrementMinutes');
12298         this.time = this.time.add(Date.MINUTE, 1);
12299         this.update();
12300     },
12301     
12302     onDecrementMinutes: function()
12303     {
12304         Roo.log('onDecrementMinutes');
12305         this.time = this.time.add(Date.MINUTE, -1);
12306         this.update();
12307     },
12308     
12309     onTogglePeriod: function()
12310     {
12311         Roo.log('onTogglePeriod');
12312         this.time = this.time.add(Date.HOUR, 12);
12313         this.update();
12314     }
12315     
12316    
12317 });
12318
12319 Roo.apply(Roo.bootstrap.TimeField,  {
12320     
12321     content : {
12322         tag: 'tbody',
12323         cn: [
12324             {
12325                 tag: 'tr',
12326                 cn: [
12327                 {
12328                     tag: 'td',
12329                     colspan: '7'
12330                 }
12331                 ]
12332             }
12333         ]
12334     },
12335     
12336     footer : {
12337         tag: 'tfoot',
12338         cn: [
12339             {
12340                 tag: 'tr',
12341                 cn: [
12342                 {
12343                     tag: 'th',
12344                     colspan: '7',
12345                     cls: '',
12346                     cn: [
12347                         {
12348                             tag: 'button',
12349                             cls: 'btn btn-info ok',
12350                             html: 'OK'
12351                         }
12352                     ]
12353                 }
12354
12355                 ]
12356             }
12357         ]
12358     }
12359 });
12360
12361 Roo.apply(Roo.bootstrap.TimeField,  {
12362   
12363     template : {
12364         tag: 'div',
12365         cls: 'datepicker dropdown-menu',
12366         cn: [
12367             {
12368                 tag: 'div',
12369                 cls: 'datepicker-time',
12370                 cn: [
12371                 {
12372                     tag: 'table',
12373                     cls: 'table-condensed',
12374                     cn:[
12375                     Roo.bootstrap.TimeField.content,
12376                     Roo.bootstrap.TimeField.footer
12377                     ]
12378                 }
12379                 ]
12380             }
12381         ]
12382     }
12383 });
12384
12385  
12386
12387  /*
12388  * - LGPL
12389  *
12390  * CheckBox
12391  * 
12392  */
12393
12394 /**
12395  * @class Roo.bootstrap.CheckBox
12396  * @extends Roo.bootstrap.Input
12397  * Bootstrap CheckBox class
12398  * 
12399  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12400  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12401  * @cfg {String} boxLabel The text that appears beside the checkbox
12402  * @cfg {Boolean} checked initnal the element
12403  * 
12404  * @constructor
12405  * Create a new CheckBox
12406  * @param {Object} config The config object
12407  */
12408
12409 Roo.bootstrap.CheckBox = function(config){
12410     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12411    
12412         this.addEvents({
12413             /**
12414             * @event check
12415             * Fires when the element is checked or unchecked.
12416             * @param {Roo.bootstrap.CheckBox} this This input
12417             * @param {Boolean} checked The new checked value
12418             */
12419            check : true
12420         });
12421 };
12422
12423 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12424     
12425     inputType: 'checkbox',
12426     inputValue: 1,
12427     valueOff: 0,
12428     boxLabel: false,
12429     checked: false,
12430     
12431     getAutoCreate : function()
12432     {
12433         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12434         
12435         var id = Roo.id();
12436         
12437         var cfg = {};
12438         
12439         cfg.cls = 'form-group' //input-group
12440         
12441         var input =  {
12442             tag: 'input',
12443             id : id,
12444             type : this.inputType,
12445             value : (!this.checked) ? this.valueOff : this.inputValue,
12446             cls : 'form-box',
12447             placeholder : this.placeholder || ''
12448             
12449         };
12450         
12451         if (this.disabled) {
12452             input.disabled=true;
12453         }
12454         
12455         if(this.checked){
12456             input.checked = this.checked;
12457         }
12458         
12459         if (this.name) {
12460             input.name = this.name;
12461         }
12462         
12463         if (this.size) {
12464             input.cls += ' input-' + this.size;
12465         }
12466         
12467         var settings=this;
12468         ['xs','sm','md','lg'].map(function(size){
12469             if (settings[size]) {
12470                 cfg.cls += ' col-' + size + '-' + settings[size];
12471             }
12472         });
12473         
12474         var inputblock = input;
12475         
12476         if (this.before || this.after) {
12477             
12478             inputblock = {
12479                 cls : 'input-group',
12480                 cn :  [] 
12481             };
12482             if (this.before) {
12483                 inputblock.cn.push({
12484                     tag :'span',
12485                     cls : 'input-group-addon',
12486                     html : this.before
12487                 });
12488             }
12489             inputblock.cn.push(input);
12490             if (this.after) {
12491                 inputblock.cn.push({
12492                     tag :'span',
12493                     cls : 'input-group-addon',
12494                     html : this.after
12495                 });
12496             }
12497             
12498         };
12499         
12500         if (align ==='left' && this.fieldLabel.length) {
12501                 Roo.log("left and has label");
12502                 cfg.cn = [
12503                     
12504                     {
12505                         tag: 'label',
12506                         'for' :  id,
12507                         cls : 'control-label col-md-' + this.labelWidth,
12508                         html : this.fieldLabel
12509                         
12510                     },
12511                     {
12512                         cls : "col-md-" + (12 - this.labelWidth), 
12513                         cn: [
12514                             inputblock
12515                         ]
12516                     }
12517                     
12518                 ];
12519         } else if ( this.fieldLabel.length) {
12520                 Roo.log(" label");
12521                 cfg.cn = [
12522                    
12523                     {
12524                         tag: this.boxLabel ? 'span' : 'label',
12525                         'for': id,
12526                         cls: 'control-label box-input-label',
12527                         //cls : 'input-group-addon',
12528                         html : this.fieldLabel
12529                         
12530                     },
12531                     
12532                     inputblock
12533                     
12534                 ];
12535
12536         } else {
12537             
12538                    Roo.log(" no label && no align");
12539                 cfg.cn = [
12540                     
12541                         inputblock
12542                     
12543                 ];
12544                 
12545                 
12546         };
12547         
12548         if(this.boxLabel){
12549             cfg.cn.push({
12550                 tag: 'label',
12551                 'for': id,
12552                 cls: 'box-label',
12553                 html: this.boxLabel
12554             })
12555         }
12556         
12557         return cfg;
12558         
12559     },
12560     
12561     /**
12562      * return the real input element.
12563      */
12564     inputEl: function ()
12565     {
12566         return this.el.select('input.form-box',true).first();
12567     },
12568     
12569     initEvents : function()
12570     {
12571 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12572         
12573         this.inputEl().on('click', this.onClick,  this);
12574         
12575     },
12576     
12577     onClick : function()
12578     {   
12579         this.setChecked(!this.checked);
12580     },
12581     
12582     setChecked : function(state,suppressEvent)
12583     {
12584         this.checked = state;
12585         
12586         this.inputEl().dom.checked = state;
12587         
12588         if(suppressEvent !== true){
12589             this.fireEvent('check', this, state);
12590         }
12591         
12592         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12593         
12594     },
12595     
12596     setValue : function(v,suppressEvent)
12597     {
12598         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12599     }
12600     
12601 });
12602
12603  
12604 /*
12605  * - LGPL
12606  *
12607  * Radio
12608  * 
12609  */
12610
12611 /**
12612  * @class Roo.bootstrap.Radio
12613  * @extends Roo.bootstrap.CheckBox
12614  * Bootstrap Radio class
12615
12616  * @constructor
12617  * Create a new Radio
12618  * @param {Object} config The config object
12619  */
12620
12621 Roo.bootstrap.Radio = function(config){
12622     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12623    
12624 };
12625
12626 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12627     
12628     inputType: 'radio',
12629     inputValue: '',
12630     valueOff: '',
12631     
12632     getAutoCreate : function()
12633     {
12634         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12635         
12636         var id = Roo.id();
12637         
12638         var cfg = {};
12639         
12640         cfg.cls = 'form-group' //input-group
12641         
12642         var input =  {
12643             tag: 'input',
12644             id : id,
12645             type : this.inputType,
12646             value : (!this.checked) ? this.valueOff : this.inputValue,
12647             cls : 'form-box',
12648             placeholder : this.placeholder || ''
12649             
12650         };
12651         
12652         if (this.disabled) {
12653             input.disabled=true;
12654         }
12655         
12656         if(this.checked){
12657             input.checked = this.checked;
12658         }
12659         
12660         if (this.name) {
12661             input.name = this.name;
12662         }
12663         
12664         if (this.size) {
12665             input.cls += ' input-' + this.size;
12666         }
12667         
12668         var settings=this;
12669         ['xs','sm','md','lg'].map(function(size){
12670             if (settings[size]) {
12671                 cfg.cls += ' col-' + size + '-' + settings[size];
12672             }
12673         });
12674         
12675         var inputblock = input;
12676         
12677         if (this.before || this.after) {
12678             
12679             inputblock = {
12680                 cls : 'input-group',
12681                 cn :  [] 
12682             };
12683             if (this.before) {
12684                 inputblock.cn.push({
12685                     tag :'span',
12686                     cls : 'input-group-addon',
12687                     html : this.before
12688                 });
12689             }
12690             inputblock.cn.push(input);
12691             if (this.after) {
12692                 inputblock.cn.push({
12693                     tag :'span',
12694                     cls : 'input-group-addon',
12695                     html : this.after
12696                 });
12697             }
12698             
12699         };
12700         
12701         if (align ==='left' && this.fieldLabel.length) {
12702                 Roo.log("left and has label");
12703                 cfg.cn = [
12704                     
12705                     {
12706                         tag: 'label',
12707                         'for' :  id,
12708                         cls : 'control-label col-md-' + this.labelWidth,
12709                         html : this.fieldLabel
12710                         
12711                     },
12712                     {
12713                         cls : "col-md-" + (12 - this.labelWidth), 
12714                         cn: [
12715                             inputblock
12716                         ]
12717                     }
12718                     
12719                 ];
12720         } else if ( this.fieldLabel.length) {
12721                 Roo.log(" label");
12722                  cfg.cn = [
12723                    
12724                     {
12725                         tag: 'label',
12726                         'for': id,
12727                         cls: 'control-label box-input-label',
12728                         //cls : 'input-group-addon',
12729                         html : this.fieldLabel
12730                         
12731                     },
12732                     
12733                     inputblock
12734                     
12735                 ];
12736
12737         } else {
12738             
12739                    Roo.log(" no label && no align");
12740                 cfg.cn = [
12741                     
12742                         inputblock
12743                     
12744                 ];
12745                 
12746                 
12747         };
12748         
12749         if(this.boxLabel){
12750             cfg.cn.push({
12751                 tag: 'label',
12752                 'for': id,
12753                 cls: 'box-label',
12754                 html: this.boxLabel
12755             })
12756         }
12757         
12758         return cfg;
12759         
12760     },
12761    
12762     onClick : function()
12763     {   
12764         this.setChecked(true);
12765     },
12766     
12767     setChecked : function(state,suppressEvent)
12768     {
12769         if(state){
12770             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12771                 v.dom.checked = false;
12772             });
12773         }
12774         
12775         this.checked = state;
12776         this.inputEl().dom.checked = state;
12777         
12778         if(suppressEvent !== true){
12779             this.fireEvent('check', this, state);
12780         }
12781         
12782         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12783         
12784     },
12785     
12786     getGroupValue : function()
12787     {
12788         var value = ''
12789         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12790             if(v.dom.checked == true){
12791                 value = v.dom.value;
12792             }
12793         });
12794         
12795         return value;
12796     },
12797     
12798     /**
12799      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12800      * @return {Mixed} value The field value
12801      */
12802     getValue : function(){
12803         return this.getGroupValue();
12804     }
12805     
12806 });
12807
12808  
12809 //<script type="text/javascript">
12810
12811 /*
12812  * Based  Ext JS Library 1.1.1
12813  * Copyright(c) 2006-2007, Ext JS, LLC.
12814  * LGPL
12815  *
12816  */
12817  
12818 /**
12819  * @class Roo.HtmlEditorCore
12820  * @extends Roo.Component
12821  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12822  *
12823  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12824  */
12825
12826 Roo.HtmlEditorCore = function(config){
12827     
12828     
12829     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12830     this.addEvents({
12831         /**
12832          * @event initialize
12833          * Fires when the editor is fully initialized (including the iframe)
12834          * @param {Roo.HtmlEditorCore} this
12835          */
12836         initialize: true,
12837         /**
12838          * @event activate
12839          * Fires when the editor is first receives the focus. Any insertion must wait
12840          * until after this event.
12841          * @param {Roo.HtmlEditorCore} this
12842          */
12843         activate: true,
12844          /**
12845          * @event beforesync
12846          * Fires before the textarea is updated with content from the editor iframe. Return false
12847          * to cancel the sync.
12848          * @param {Roo.HtmlEditorCore} this
12849          * @param {String} html
12850          */
12851         beforesync: true,
12852          /**
12853          * @event beforepush
12854          * Fires before the iframe editor is updated with content from the textarea. Return false
12855          * to cancel the push.
12856          * @param {Roo.HtmlEditorCore} this
12857          * @param {String} html
12858          */
12859         beforepush: true,
12860          /**
12861          * @event sync
12862          * Fires when the textarea is updated with content from the editor iframe.
12863          * @param {Roo.HtmlEditorCore} this
12864          * @param {String} html
12865          */
12866         sync: true,
12867          /**
12868          * @event push
12869          * Fires when the iframe editor is updated with content from the textarea.
12870          * @param {Roo.HtmlEditorCore} this
12871          * @param {String} html
12872          */
12873         push: true,
12874         
12875         /**
12876          * @event editorevent
12877          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12878          * @param {Roo.HtmlEditorCore} this
12879          */
12880         editorevent: true
12881     });
12882      
12883 };
12884
12885
12886 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12887
12888
12889      /**
12890      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12891      */
12892     
12893     owner : false,
12894     
12895      /**
12896      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12897      *                        Roo.resizable.
12898      */
12899     resizable : false,
12900      /**
12901      * @cfg {Number} height (in pixels)
12902      */   
12903     height: 300,
12904    /**
12905      * @cfg {Number} width (in pixels)
12906      */   
12907     width: 500,
12908     
12909     /**
12910      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12911      * 
12912      */
12913     stylesheets: false,
12914     
12915     // id of frame..
12916     frameId: false,
12917     
12918     // private properties
12919     validationEvent : false,
12920     deferHeight: true,
12921     initialized : false,
12922     activated : false,
12923     sourceEditMode : false,
12924     onFocus : Roo.emptyFn,
12925     iframePad:3,
12926     hideMode:'offsets',
12927     
12928     clearUp: true,
12929     
12930      
12931     
12932
12933     /**
12934      * Protected method that will not generally be called directly. It
12935      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12936      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12937      */
12938     getDocMarkup : function(){
12939         // body styles..
12940         var st = '';
12941         Roo.log(this.stylesheets);
12942         
12943         // inherit styels from page...?? 
12944         if (this.stylesheets === false) {
12945             
12946             Roo.get(document.head).select('style').each(function(node) {
12947                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12948             });
12949             
12950             Roo.get(document.head).select('link').each(function(node) { 
12951                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12952             });
12953             
12954         } else if (!this.stylesheets.length) {
12955                 // simple..
12956                 st = '<style type="text/css">' +
12957                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12958                    '</style>';
12959         } else {
12960             Roo.each(this.stylesheets, function(s) {
12961                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12962             });
12963             
12964         }
12965         
12966         st +=  '<style type="text/css">' +
12967             'IMG { cursor: pointer } ' +
12968         '</style>';
12969
12970         
12971         return '<html><head>' + st  +
12972             //<style type="text/css">' +
12973             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12974             //'</style>' +
12975             ' </head><body class="roo-htmleditor-body"></body></html>';
12976     },
12977
12978     // private
12979     onRender : function(ct, position)
12980     {
12981         var _t = this;
12982         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12983         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12984         
12985         
12986         this.el.dom.style.border = '0 none';
12987         this.el.dom.setAttribute('tabIndex', -1);
12988         this.el.addClass('x-hidden hide');
12989         
12990         
12991         
12992         if(Roo.isIE){ // fix IE 1px bogus margin
12993             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12994         }
12995        
12996         
12997         this.frameId = Roo.id();
12998         
12999          
13000         
13001         var iframe = this.owner.wrap.createChild({
13002             tag: 'iframe',
13003             cls: 'form-control', // bootstrap..
13004             id: this.frameId,
13005             name: this.frameId,
13006             frameBorder : 'no',
13007             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13008         }, this.el
13009         );
13010         
13011         
13012         this.iframe = iframe.dom;
13013
13014          this.assignDocWin();
13015         
13016         this.doc.designMode = 'on';
13017        
13018         this.doc.open();
13019         this.doc.write(this.getDocMarkup());
13020         this.doc.close();
13021
13022         
13023         var task = { // must defer to wait for browser to be ready
13024             run : function(){
13025                 //console.log("run task?" + this.doc.readyState);
13026                 this.assignDocWin();
13027                 if(this.doc.body || this.doc.readyState == 'complete'){
13028                     try {
13029                         this.doc.designMode="on";
13030                     } catch (e) {
13031                         return;
13032                     }
13033                     Roo.TaskMgr.stop(task);
13034                     this.initEditor.defer(10, this);
13035                 }
13036             },
13037             interval : 10,
13038             duration: 10000,
13039             scope: this
13040         };
13041         Roo.TaskMgr.start(task);
13042
13043         
13044          
13045     },
13046
13047     // private
13048     onResize : function(w, h)
13049     {
13050          Roo.log('resize: ' +w + ',' + h );
13051         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13052         if(!this.iframe){
13053             return;
13054         }
13055         if(typeof w == 'number'){
13056             
13057             this.iframe.style.width = w + 'px';
13058         }
13059         if(typeof h == 'number'){
13060             
13061             this.iframe.style.height = h + 'px';
13062             if(this.doc){
13063                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13064             }
13065         }
13066         
13067     },
13068
13069     /**
13070      * Toggles the editor between standard and source edit mode.
13071      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13072      */
13073     toggleSourceEdit : function(sourceEditMode){
13074         
13075         this.sourceEditMode = sourceEditMode === true;
13076         
13077         if(this.sourceEditMode){
13078  
13079             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13080             
13081         }else{
13082             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13083             //this.iframe.className = '';
13084             this.deferFocus();
13085         }
13086         //this.setSize(this.owner.wrap.getSize());
13087         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13088     },
13089
13090     
13091   
13092
13093     /**
13094      * Protected method that will not generally be called directly. If you need/want
13095      * custom HTML cleanup, this is the method you should override.
13096      * @param {String} html The HTML to be cleaned
13097      * return {String} The cleaned HTML
13098      */
13099     cleanHtml : function(html){
13100         html = String(html);
13101         if(html.length > 5){
13102             if(Roo.isSafari){ // strip safari nonsense
13103                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13104             }
13105         }
13106         if(html == '&nbsp;'){
13107             html = '';
13108         }
13109         return html;
13110     },
13111
13112     /**
13113      * HTML Editor -> Textarea
13114      * Protected method that will not generally be called directly. Syncs the contents
13115      * of the editor iframe with the textarea.
13116      */
13117     syncValue : function(){
13118         if(this.initialized){
13119             var bd = (this.doc.body || this.doc.documentElement);
13120             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13121             var html = bd.innerHTML;
13122             if(Roo.isSafari){
13123                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13124                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13125                 if(m && m[1]){
13126                     html = '<div style="'+m[0]+'">' + html + '</div>';
13127                 }
13128             }
13129             html = this.cleanHtml(html);
13130             // fix up the special chars.. normaly like back quotes in word...
13131             // however we do not want to do this with chinese..
13132             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13133                 var cc = b.charCodeAt();
13134                 if (
13135                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13136                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13137                     (cc >= 0xf900 && cc < 0xfb00 )
13138                 ) {
13139                         return b;
13140                 }
13141                 return "&#"+cc+";" 
13142             });
13143             if(this.owner.fireEvent('beforesync', this, html) !== false){
13144                 this.el.dom.value = html;
13145                 this.owner.fireEvent('sync', this, html);
13146             }
13147         }
13148     },
13149
13150     /**
13151      * Protected method that will not generally be called directly. Pushes the value of the textarea
13152      * into the iframe editor.
13153      */
13154     pushValue : function(){
13155         if(this.initialized){
13156             var v = this.el.dom.value.trim();
13157             
13158 //            if(v.length < 1){
13159 //                v = '&#160;';
13160 //            }
13161             
13162             if(this.owner.fireEvent('beforepush', this, v) !== false){
13163                 var d = (this.doc.body || this.doc.documentElement);
13164                 d.innerHTML = v;
13165                 this.cleanUpPaste();
13166                 this.el.dom.value = d.innerHTML;
13167                 this.owner.fireEvent('push', this, v);
13168             }
13169         }
13170     },
13171
13172     // private
13173     deferFocus : function(){
13174         this.focus.defer(10, this);
13175     },
13176
13177     // doc'ed in Field
13178     focus : function(){
13179         if(this.win && !this.sourceEditMode){
13180             this.win.focus();
13181         }else{
13182             this.el.focus();
13183         }
13184     },
13185     
13186     assignDocWin: function()
13187     {
13188         var iframe = this.iframe;
13189         
13190          if(Roo.isIE){
13191             this.doc = iframe.contentWindow.document;
13192             this.win = iframe.contentWindow;
13193         } else {
13194             if (!Roo.get(this.frameId)) {
13195                 return;
13196             }
13197             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13198             this.win = Roo.get(this.frameId).dom.contentWindow;
13199         }
13200     },
13201     
13202     // private
13203     initEditor : function(){
13204         //console.log("INIT EDITOR");
13205         this.assignDocWin();
13206         
13207         
13208         
13209         this.doc.designMode="on";
13210         this.doc.open();
13211         this.doc.write(this.getDocMarkup());
13212         this.doc.close();
13213         
13214         var dbody = (this.doc.body || this.doc.documentElement);
13215         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13216         // this copies styles from the containing element into thsi one..
13217         // not sure why we need all of this..
13218         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13219         ss['background-attachment'] = 'fixed'; // w3c
13220         dbody.bgProperties = 'fixed'; // ie
13221         Roo.DomHelper.applyStyles(dbody, ss);
13222         Roo.EventManager.on(this.doc, {
13223             //'mousedown': this.onEditorEvent,
13224             'mouseup': this.onEditorEvent,
13225             'dblclick': this.onEditorEvent,
13226             'click': this.onEditorEvent,
13227             'keyup': this.onEditorEvent,
13228             buffer:100,
13229             scope: this
13230         });
13231         if(Roo.isGecko){
13232             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13233         }
13234         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13235             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13236         }
13237         this.initialized = true;
13238
13239         this.owner.fireEvent('initialize', this);
13240         this.pushValue();
13241     },
13242
13243     // private
13244     onDestroy : function(){
13245         
13246         
13247         
13248         if(this.rendered){
13249             
13250             //for (var i =0; i < this.toolbars.length;i++) {
13251             //    // fixme - ask toolbars for heights?
13252             //    this.toolbars[i].onDestroy();
13253            // }
13254             
13255             //this.wrap.dom.innerHTML = '';
13256             //this.wrap.remove();
13257         }
13258     },
13259
13260     // private
13261     onFirstFocus : function(){
13262         
13263         this.assignDocWin();
13264         
13265         
13266         this.activated = true;
13267          
13268     
13269         if(Roo.isGecko){ // prevent silly gecko errors
13270             this.win.focus();
13271             var s = this.win.getSelection();
13272             if(!s.focusNode || s.focusNode.nodeType != 3){
13273                 var r = s.getRangeAt(0);
13274                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13275                 r.collapse(true);
13276                 this.deferFocus();
13277             }
13278             try{
13279                 this.execCmd('useCSS', true);
13280                 this.execCmd('styleWithCSS', false);
13281             }catch(e){}
13282         }
13283         this.owner.fireEvent('activate', this);
13284     },
13285
13286     // private
13287     adjustFont: function(btn){
13288         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13289         //if(Roo.isSafari){ // safari
13290         //    adjust *= 2;
13291        // }
13292         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13293         if(Roo.isSafari){ // safari
13294             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13295             v =  (v < 10) ? 10 : v;
13296             v =  (v > 48) ? 48 : v;
13297             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13298             
13299         }
13300         
13301         
13302         v = Math.max(1, v+adjust);
13303         
13304         this.execCmd('FontSize', v  );
13305     },
13306
13307     onEditorEvent : function(e){
13308         this.owner.fireEvent('editorevent', this, e);
13309       //  this.updateToolbar();
13310         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13311     },
13312
13313     insertTag : function(tg)
13314     {
13315         // could be a bit smarter... -> wrap the current selected tRoo..
13316         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13317             
13318             range = this.createRange(this.getSelection());
13319             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13320             wrappingNode.appendChild(range.extractContents());
13321             range.insertNode(wrappingNode);
13322
13323             return;
13324             
13325             
13326             
13327         }
13328         this.execCmd("formatblock",   tg);
13329         
13330     },
13331     
13332     insertText : function(txt)
13333     {
13334         
13335         
13336         var range = this.createRange();
13337         range.deleteContents();
13338                //alert(Sender.getAttribute('label'));
13339                
13340         range.insertNode(this.doc.createTextNode(txt));
13341     } ,
13342     
13343      
13344
13345     /**
13346      * Executes a Midas editor command on the editor document and performs necessary focus and
13347      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13348      * @param {String} cmd The Midas command
13349      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13350      */
13351     relayCmd : function(cmd, value){
13352         this.win.focus();
13353         this.execCmd(cmd, value);
13354         this.owner.fireEvent('editorevent', this);
13355         //this.updateToolbar();
13356         this.owner.deferFocus();
13357     },
13358
13359     /**
13360      * Executes a Midas editor command directly on the editor document.
13361      * For visual commands, you should use {@link #relayCmd} instead.
13362      * <b>This should only be called after the editor is initialized.</b>
13363      * @param {String} cmd The Midas command
13364      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13365      */
13366     execCmd : function(cmd, value){
13367         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13368         this.syncValue();
13369     },
13370  
13371  
13372    
13373     /**
13374      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13375      * to insert tRoo.
13376      * @param {String} text | dom node.. 
13377      */
13378     insertAtCursor : function(text)
13379     {
13380         
13381         
13382         
13383         if(!this.activated){
13384             return;
13385         }
13386         /*
13387         if(Roo.isIE){
13388             this.win.focus();
13389             var r = this.doc.selection.createRange();
13390             if(r){
13391                 r.collapse(true);
13392                 r.pasteHTML(text);
13393                 this.syncValue();
13394                 this.deferFocus();
13395             
13396             }
13397             return;
13398         }
13399         */
13400         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13401             this.win.focus();
13402             
13403             
13404             // from jquery ui (MIT licenced)
13405             var range, node;
13406             var win = this.win;
13407             
13408             if (win.getSelection && win.getSelection().getRangeAt) {
13409                 range = win.getSelection().getRangeAt(0);
13410                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13411                 range.insertNode(node);
13412             } else if (win.document.selection && win.document.selection.createRange) {
13413                 // no firefox support
13414                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13415                 win.document.selection.createRange().pasteHTML(txt);
13416             } else {
13417                 // no firefox support
13418                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13419                 this.execCmd('InsertHTML', txt);
13420             } 
13421             
13422             this.syncValue();
13423             
13424             this.deferFocus();
13425         }
13426     },
13427  // private
13428     mozKeyPress : function(e){
13429         if(e.ctrlKey){
13430             var c = e.getCharCode(), cmd;
13431           
13432             if(c > 0){
13433                 c = String.fromCharCode(c).toLowerCase();
13434                 switch(c){
13435                     case 'b':
13436                         cmd = 'bold';
13437                         break;
13438                     case 'i':
13439                         cmd = 'italic';
13440                         break;
13441                     
13442                     case 'u':
13443                         cmd = 'underline';
13444                         break;
13445                     
13446                     case 'v':
13447                         this.cleanUpPaste.defer(100, this);
13448                         return;
13449                         
13450                 }
13451                 if(cmd){
13452                     this.win.focus();
13453                     this.execCmd(cmd);
13454                     this.deferFocus();
13455                     e.preventDefault();
13456                 }
13457                 
13458             }
13459         }
13460     },
13461
13462     // private
13463     fixKeys : function(){ // load time branching for fastest keydown performance
13464         if(Roo.isIE){
13465             return function(e){
13466                 var k = e.getKey(), r;
13467                 if(k == e.TAB){
13468                     e.stopEvent();
13469                     r = this.doc.selection.createRange();
13470                     if(r){
13471                         r.collapse(true);
13472                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13473                         this.deferFocus();
13474                     }
13475                     return;
13476                 }
13477                 
13478                 if(k == e.ENTER){
13479                     r = this.doc.selection.createRange();
13480                     if(r){
13481                         var target = r.parentElement();
13482                         if(!target || target.tagName.toLowerCase() != 'li'){
13483                             e.stopEvent();
13484                             r.pasteHTML('<br />');
13485                             r.collapse(false);
13486                             r.select();
13487                         }
13488                     }
13489                 }
13490                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13491                     this.cleanUpPaste.defer(100, this);
13492                     return;
13493                 }
13494                 
13495                 
13496             };
13497         }else if(Roo.isOpera){
13498             return function(e){
13499                 var k = e.getKey();
13500                 if(k == e.TAB){
13501                     e.stopEvent();
13502                     this.win.focus();
13503                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13504                     this.deferFocus();
13505                 }
13506                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13507                     this.cleanUpPaste.defer(100, this);
13508                     return;
13509                 }
13510                 
13511             };
13512         }else if(Roo.isSafari){
13513             return function(e){
13514                 var k = e.getKey();
13515                 
13516                 if(k == e.TAB){
13517                     e.stopEvent();
13518                     this.execCmd('InsertText','\t');
13519                     this.deferFocus();
13520                     return;
13521                 }
13522                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13523                     this.cleanUpPaste.defer(100, this);
13524                     return;
13525                 }
13526                 
13527              };
13528         }
13529     }(),
13530     
13531     getAllAncestors: function()
13532     {
13533         var p = this.getSelectedNode();
13534         var a = [];
13535         if (!p) {
13536             a.push(p); // push blank onto stack..
13537             p = this.getParentElement();
13538         }
13539         
13540         
13541         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13542             a.push(p);
13543             p = p.parentNode;
13544         }
13545         a.push(this.doc.body);
13546         return a;
13547     },
13548     lastSel : false,
13549     lastSelNode : false,
13550     
13551     
13552     getSelection : function() 
13553     {
13554         this.assignDocWin();
13555         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13556     },
13557     
13558     getSelectedNode: function() 
13559     {
13560         // this may only work on Gecko!!!
13561         
13562         // should we cache this!!!!
13563         
13564         
13565         
13566          
13567         var range = this.createRange(this.getSelection()).cloneRange();
13568         
13569         if (Roo.isIE) {
13570             var parent = range.parentElement();
13571             while (true) {
13572                 var testRange = range.duplicate();
13573                 testRange.moveToElementText(parent);
13574                 if (testRange.inRange(range)) {
13575                     break;
13576                 }
13577                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13578                     break;
13579                 }
13580                 parent = parent.parentElement;
13581             }
13582             return parent;
13583         }
13584         
13585         // is ancestor a text element.
13586         var ac =  range.commonAncestorContainer;
13587         if (ac.nodeType == 3) {
13588             ac = ac.parentNode;
13589         }
13590         
13591         var ar = ac.childNodes;
13592          
13593         var nodes = [];
13594         var other_nodes = [];
13595         var has_other_nodes = false;
13596         for (var i=0;i<ar.length;i++) {
13597             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13598                 continue;
13599             }
13600             // fullly contained node.
13601             
13602             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13603                 nodes.push(ar[i]);
13604                 continue;
13605             }
13606             
13607             // probably selected..
13608             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13609                 other_nodes.push(ar[i]);
13610                 continue;
13611             }
13612             // outer..
13613             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13614                 continue;
13615             }
13616             
13617             
13618             has_other_nodes = true;
13619         }
13620         if (!nodes.length && other_nodes.length) {
13621             nodes= other_nodes;
13622         }
13623         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13624             return false;
13625         }
13626         
13627         return nodes[0];
13628     },
13629     createRange: function(sel)
13630     {
13631         // this has strange effects when using with 
13632         // top toolbar - not sure if it's a great idea.
13633         //this.editor.contentWindow.focus();
13634         if (typeof sel != "undefined") {
13635             try {
13636                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13637             } catch(e) {
13638                 return this.doc.createRange();
13639             }
13640         } else {
13641             return this.doc.createRange();
13642         }
13643     },
13644     getParentElement: function()
13645     {
13646         
13647         this.assignDocWin();
13648         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13649         
13650         var range = this.createRange(sel);
13651          
13652         try {
13653             var p = range.commonAncestorContainer;
13654             while (p.nodeType == 3) { // text node
13655                 p = p.parentNode;
13656             }
13657             return p;
13658         } catch (e) {
13659             return null;
13660         }
13661     
13662     },
13663     /***
13664      *
13665      * Range intersection.. the hard stuff...
13666      *  '-1' = before
13667      *  '0' = hits..
13668      *  '1' = after.
13669      *         [ -- selected range --- ]
13670      *   [fail]                        [fail]
13671      *
13672      *    basically..
13673      *      if end is before start or  hits it. fail.
13674      *      if start is after end or hits it fail.
13675      *
13676      *   if either hits (but other is outside. - then it's not 
13677      *   
13678      *    
13679      **/
13680     
13681     
13682     // @see http://www.thismuchiknow.co.uk/?p=64.
13683     rangeIntersectsNode : function(range, node)
13684     {
13685         var nodeRange = node.ownerDocument.createRange();
13686         try {
13687             nodeRange.selectNode(node);
13688         } catch (e) {
13689             nodeRange.selectNodeContents(node);
13690         }
13691     
13692         var rangeStartRange = range.cloneRange();
13693         rangeStartRange.collapse(true);
13694     
13695         var rangeEndRange = range.cloneRange();
13696         rangeEndRange.collapse(false);
13697     
13698         var nodeStartRange = nodeRange.cloneRange();
13699         nodeStartRange.collapse(true);
13700     
13701         var nodeEndRange = nodeRange.cloneRange();
13702         nodeEndRange.collapse(false);
13703     
13704         return rangeStartRange.compareBoundaryPoints(
13705                  Range.START_TO_START, nodeEndRange) == -1 &&
13706                rangeEndRange.compareBoundaryPoints(
13707                  Range.START_TO_START, nodeStartRange) == 1;
13708         
13709          
13710     },
13711     rangeCompareNode : function(range, node)
13712     {
13713         var nodeRange = node.ownerDocument.createRange();
13714         try {
13715             nodeRange.selectNode(node);
13716         } catch (e) {
13717             nodeRange.selectNodeContents(node);
13718         }
13719         
13720         
13721         range.collapse(true);
13722     
13723         nodeRange.collapse(true);
13724      
13725         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13726         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13727          
13728         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13729         
13730         var nodeIsBefore   =  ss == 1;
13731         var nodeIsAfter    = ee == -1;
13732         
13733         if (nodeIsBefore && nodeIsAfter)
13734             return 0; // outer
13735         if (!nodeIsBefore && nodeIsAfter)
13736             return 1; //right trailed.
13737         
13738         if (nodeIsBefore && !nodeIsAfter)
13739             return 2;  // left trailed.
13740         // fully contined.
13741         return 3;
13742     },
13743
13744     // private? - in a new class?
13745     cleanUpPaste :  function()
13746     {
13747         // cleans up the whole document..
13748         Roo.log('cleanuppaste');
13749         
13750         this.cleanUpChildren(this.doc.body);
13751         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13752         if (clean != this.doc.body.innerHTML) {
13753             this.doc.body.innerHTML = clean;
13754         }
13755         
13756     },
13757     
13758     cleanWordChars : function(input) {// change the chars to hex code
13759         var he = Roo.HtmlEditorCore;
13760         
13761         var output = input;
13762         Roo.each(he.swapCodes, function(sw) { 
13763             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13764             
13765             output = output.replace(swapper, sw[1]);
13766         });
13767         
13768         return output;
13769     },
13770     
13771     
13772     cleanUpChildren : function (n)
13773     {
13774         if (!n.childNodes.length) {
13775             return;
13776         }
13777         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13778            this.cleanUpChild(n.childNodes[i]);
13779         }
13780     },
13781     
13782     
13783         
13784     
13785     cleanUpChild : function (node)
13786     {
13787         var ed = this;
13788         //console.log(node);
13789         if (node.nodeName == "#text") {
13790             // clean up silly Windows -- stuff?
13791             return; 
13792         }
13793         if (node.nodeName == "#comment") {
13794             node.parentNode.removeChild(node);
13795             // clean up silly Windows -- stuff?
13796             return; 
13797         }
13798         
13799         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13800             // remove node.
13801             node.parentNode.removeChild(node);
13802             return;
13803             
13804         }
13805         
13806         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13807         
13808         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13809         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13810         
13811         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13812         //    remove_keep_children = true;
13813         //}
13814         
13815         if (remove_keep_children) {
13816             this.cleanUpChildren(node);
13817             // inserts everything just before this node...
13818             while (node.childNodes.length) {
13819                 var cn = node.childNodes[0];
13820                 node.removeChild(cn);
13821                 node.parentNode.insertBefore(cn, node);
13822             }
13823             node.parentNode.removeChild(node);
13824             return;
13825         }
13826         
13827         if (!node.attributes || !node.attributes.length) {
13828             this.cleanUpChildren(node);
13829             return;
13830         }
13831         
13832         function cleanAttr(n,v)
13833         {
13834             
13835             if (v.match(/^\./) || v.match(/^\//)) {
13836                 return;
13837             }
13838             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13839                 return;
13840             }
13841             if (v.match(/^#/)) {
13842                 return;
13843             }
13844 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13845             node.removeAttribute(n);
13846             
13847         }
13848         
13849         function cleanStyle(n,v)
13850         {
13851             if (v.match(/expression/)) { //XSS?? should we even bother..
13852                 node.removeAttribute(n);
13853                 return;
13854             }
13855             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13856             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13857             
13858             
13859             var parts = v.split(/;/);
13860             var clean = [];
13861             
13862             Roo.each(parts, function(p) {
13863                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13864                 if (!p.length) {
13865                     return true;
13866                 }
13867                 var l = p.split(':').shift().replace(/\s+/g,'');
13868                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13869                 
13870                 if ( cblack.indexOf(l) > -1) {
13871 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13872                     //node.removeAttribute(n);
13873                     return true;
13874                 }
13875                 //Roo.log()
13876                 // only allow 'c whitelisted system attributes'
13877                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13878 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13879                     //node.removeAttribute(n);
13880                     return true;
13881                 }
13882                 
13883                 
13884                  
13885                 
13886                 clean.push(p);
13887                 return true;
13888             });
13889             if (clean.length) { 
13890                 node.setAttribute(n, clean.join(';'));
13891             } else {
13892                 node.removeAttribute(n);
13893             }
13894             
13895         }
13896         
13897         
13898         for (var i = node.attributes.length-1; i > -1 ; i--) {
13899             var a = node.attributes[i];
13900             //console.log(a);
13901             
13902             if (a.name.toLowerCase().substr(0,2)=='on')  {
13903                 node.removeAttribute(a.name);
13904                 continue;
13905             }
13906             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13907                 node.removeAttribute(a.name);
13908                 continue;
13909             }
13910             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13911                 cleanAttr(a.name,a.value); // fixme..
13912                 continue;
13913             }
13914             if (a.name == 'style') {
13915                 cleanStyle(a.name,a.value);
13916                 continue;
13917             }
13918             /// clean up MS crap..
13919             // tecnically this should be a list of valid class'es..
13920             
13921             
13922             if (a.name == 'class') {
13923                 if (a.value.match(/^Mso/)) {
13924                     node.className = '';
13925                 }
13926                 
13927                 if (a.value.match(/body/)) {
13928                     node.className = '';
13929                 }
13930                 continue;
13931             }
13932             
13933             // style cleanup!?
13934             // class cleanup?
13935             
13936         }
13937         
13938         
13939         this.cleanUpChildren(node);
13940         
13941         
13942     }
13943     
13944     
13945     // hide stuff that is not compatible
13946     /**
13947      * @event blur
13948      * @hide
13949      */
13950     /**
13951      * @event change
13952      * @hide
13953      */
13954     /**
13955      * @event focus
13956      * @hide
13957      */
13958     /**
13959      * @event specialkey
13960      * @hide
13961      */
13962     /**
13963      * @cfg {String} fieldClass @hide
13964      */
13965     /**
13966      * @cfg {String} focusClass @hide
13967      */
13968     /**
13969      * @cfg {String} autoCreate @hide
13970      */
13971     /**
13972      * @cfg {String} inputType @hide
13973      */
13974     /**
13975      * @cfg {String} invalidClass @hide
13976      */
13977     /**
13978      * @cfg {String} invalidText @hide
13979      */
13980     /**
13981      * @cfg {String} msgFx @hide
13982      */
13983     /**
13984      * @cfg {String} validateOnBlur @hide
13985      */
13986 });
13987
13988 Roo.HtmlEditorCore.white = [
13989         'area', 'br', 'img', 'input', 'hr', 'wbr',
13990         
13991        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
13992        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
13993        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
13994        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
13995        'table',   'ul',         'xmp', 
13996        
13997        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
13998       'thead',   'tr', 
13999      
14000       'dir', 'menu', 'ol', 'ul', 'dl',
14001        
14002       'embed',  'object'
14003 ];
14004
14005
14006 Roo.HtmlEditorCore.black = [
14007     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14008         'applet', // 
14009         'base',   'basefont', 'bgsound', 'blink',  'body', 
14010         'frame',  'frameset', 'head',    'html',   'ilayer', 
14011         'iframe', 'layer',  'link',     'meta',    'object',   
14012         'script', 'style' ,'title',  'xml' // clean later..
14013 ];
14014 Roo.HtmlEditorCore.clean = [
14015     'script', 'style', 'title', 'xml'
14016 ];
14017 Roo.HtmlEditorCore.remove = [
14018     'font'
14019 ];
14020 // attributes..
14021
14022 Roo.HtmlEditorCore.ablack = [
14023     'on'
14024 ];
14025     
14026 Roo.HtmlEditorCore.aclean = [ 
14027     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14028 ];
14029
14030 // protocols..
14031 Roo.HtmlEditorCore.pwhite= [
14032         'http',  'https',  'mailto'
14033 ];
14034
14035 // white listed style attributes.
14036 Roo.HtmlEditorCore.cwhite= [
14037       //  'text-align', /// default is to allow most things..
14038       
14039          
14040 //        'font-size'//??
14041 ];
14042
14043 // black listed style attributes.
14044 Roo.HtmlEditorCore.cblack= [
14045       //  'font-size' -- this can be set by the project 
14046 ];
14047
14048
14049 Roo.HtmlEditorCore.swapCodes   =[ 
14050     [    8211, "--" ], 
14051     [    8212, "--" ], 
14052     [    8216,  "'" ],  
14053     [    8217, "'" ],  
14054     [    8220, '"' ],  
14055     [    8221, '"' ],  
14056     [    8226, "*" ],  
14057     [    8230, "..." ]
14058 ]; 
14059
14060     /*
14061  * - LGPL
14062  *
14063  * HtmlEditor
14064  * 
14065  */
14066
14067 /**
14068  * @class Roo.bootstrap.HtmlEditor
14069  * @extends Roo.bootstrap.TextArea
14070  * Bootstrap HtmlEditor class
14071
14072  * @constructor
14073  * Create a new HtmlEditor
14074  * @param {Object} config The config object
14075  */
14076
14077 Roo.bootstrap.HtmlEditor = function(config){
14078     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14079     if (!this.toolbars) {
14080         this.toolbars = [];
14081     }
14082     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14083     this.addEvents({
14084             /**
14085              * @event initialize
14086              * Fires when the editor is fully initialized (including the iframe)
14087              * @param {HtmlEditor} this
14088              */
14089             initialize: true,
14090             /**
14091              * @event activate
14092              * Fires when the editor is first receives the focus. Any insertion must wait
14093              * until after this event.
14094              * @param {HtmlEditor} this
14095              */
14096             activate: true,
14097              /**
14098              * @event beforesync
14099              * Fires before the textarea is updated with content from the editor iframe. Return false
14100              * to cancel the sync.
14101              * @param {HtmlEditor} this
14102              * @param {String} html
14103              */
14104             beforesync: true,
14105              /**
14106              * @event beforepush
14107              * Fires before the iframe editor is updated with content from the textarea. Return false
14108              * to cancel the push.
14109              * @param {HtmlEditor} this
14110              * @param {String} html
14111              */
14112             beforepush: true,
14113              /**
14114              * @event sync
14115              * Fires when the textarea is updated with content from the editor iframe.
14116              * @param {HtmlEditor} this
14117              * @param {String} html
14118              */
14119             sync: true,
14120              /**
14121              * @event push
14122              * Fires when the iframe editor is updated with content from the textarea.
14123              * @param {HtmlEditor} this
14124              * @param {String} html
14125              */
14126             push: true,
14127              /**
14128              * @event editmodechange
14129              * Fires when the editor switches edit modes
14130              * @param {HtmlEditor} this
14131              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14132              */
14133             editmodechange: true,
14134             /**
14135              * @event editorevent
14136              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14137              * @param {HtmlEditor} this
14138              */
14139             editorevent: true,
14140             /**
14141              * @event firstfocus
14142              * Fires when on first focus - needed by toolbars..
14143              * @param {HtmlEditor} this
14144              */
14145             firstfocus: true,
14146             /**
14147              * @event autosave
14148              * Auto save the htmlEditor value as a file into Events
14149              * @param {HtmlEditor} this
14150              */
14151             autosave: true,
14152             /**
14153              * @event savedpreview
14154              * preview the saved version of htmlEditor
14155              * @param {HtmlEditor} this
14156              */
14157             savedpreview: true
14158         });
14159 };
14160
14161
14162 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14163     
14164     
14165       /**
14166      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14167      */
14168     toolbars : false,
14169    
14170      /**
14171      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14172      *                        Roo.resizable.
14173      */
14174     resizable : false,
14175      /**
14176      * @cfg {Number} height (in pixels)
14177      */   
14178     height: 300,
14179    /**
14180      * @cfg {Number} width (in pixels)
14181      */   
14182     width: false,
14183     
14184     /**
14185      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14186      * 
14187      */
14188     stylesheets: false,
14189     
14190     // id of frame..
14191     frameId: false,
14192     
14193     // private properties
14194     validationEvent : false,
14195     deferHeight: true,
14196     initialized : false,
14197     activated : false,
14198     
14199     onFocus : Roo.emptyFn,
14200     iframePad:3,
14201     hideMode:'offsets',
14202     
14203     
14204     tbContainer : false,
14205     
14206     toolbarContainer :function() {
14207         return this.wrap.select('.x-html-editor-tb',true).first();
14208     },
14209
14210     /**
14211      * Protected method that will not generally be called directly. It
14212      * is called when the editor creates its toolbar. Override this method if you need to
14213      * add custom toolbar buttons.
14214      * @param {HtmlEditor} editor
14215      */
14216     createToolbar : function(){
14217         
14218         Roo.log("create toolbars");
14219         
14220         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14221         this.toolbars[0].render(this.toolbarContainer());
14222         
14223         return;
14224         
14225 //        if (!editor.toolbars || !editor.toolbars.length) {
14226 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14227 //        }
14228 //        
14229 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14230 //            editor.toolbars[i] = Roo.factory(
14231 //                    typeof(editor.toolbars[i]) == 'string' ?
14232 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14233 //                Roo.bootstrap.HtmlEditor);
14234 //            editor.toolbars[i].init(editor);
14235 //        }
14236     },
14237
14238      
14239     // private
14240     onRender : function(ct, position)
14241     {
14242        // Roo.log("Call onRender: " + this.xtype);
14243         var _t = this;
14244         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14245       
14246         this.wrap = this.inputEl().wrap({
14247             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14248         });
14249         
14250         this.editorcore.onRender(ct, position);
14251          
14252         if (this.resizable) {
14253             this.resizeEl = new Roo.Resizable(this.wrap, {
14254                 pinned : true,
14255                 wrap: true,
14256                 dynamic : true,
14257                 minHeight : this.height,
14258                 height: this.height,
14259                 handles : this.resizable,
14260                 width: this.width,
14261                 listeners : {
14262                     resize : function(r, w, h) {
14263                         _t.onResize(w,h); // -something
14264                     }
14265                 }
14266             });
14267             
14268         }
14269         this.createToolbar(this);
14270        
14271         
14272         if(!this.width && this.resizable){
14273             this.setSize(this.wrap.getSize());
14274         }
14275         if (this.resizeEl) {
14276             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14277             // should trigger onReize..
14278         }
14279         
14280     },
14281
14282     // private
14283     onResize : function(w, h)
14284     {
14285         Roo.log('resize: ' +w + ',' + h );
14286         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14287         var ew = false;
14288         var eh = false;
14289         
14290         if(this.inputEl() ){
14291             if(typeof w == 'number'){
14292                 var aw = w - this.wrap.getFrameWidth('lr');
14293                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14294                 ew = aw;
14295             }
14296             if(typeof h == 'number'){
14297                  var tbh = -11;  // fixme it needs to tool bar size!
14298                 for (var i =0; i < this.toolbars.length;i++) {
14299                     // fixme - ask toolbars for heights?
14300                     tbh += this.toolbars[i].el.getHeight();
14301                     //if (this.toolbars[i].footer) {
14302                     //    tbh += this.toolbars[i].footer.el.getHeight();
14303                     //}
14304                 }
14305               
14306                 
14307                 
14308                 
14309                 
14310                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14311                 ah -= 5; // knock a few pixes off for look..
14312                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14313                 var eh = ah;
14314             }
14315         }
14316         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14317         this.editorcore.onResize(ew,eh);
14318         
14319     },
14320
14321     /**
14322      * Toggles the editor between standard and source edit mode.
14323      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14324      */
14325     toggleSourceEdit : function(sourceEditMode)
14326     {
14327         this.editorcore.toggleSourceEdit(sourceEditMode);
14328         
14329         if(this.editorcore.sourceEditMode){
14330             Roo.log('editor - showing textarea');
14331             
14332 //            Roo.log('in');
14333 //            Roo.log(this.syncValue());
14334             this.syncValue();
14335             this.inputEl().removeClass('hide');
14336             this.inputEl().dom.removeAttribute('tabIndex');
14337             this.inputEl().focus();
14338         }else{
14339             Roo.log('editor - hiding textarea');
14340 //            Roo.log('out')
14341 //            Roo.log(this.pushValue()); 
14342             this.pushValue();
14343             
14344             this.inputEl().addClass('hide');
14345             this.inputEl().dom.setAttribute('tabIndex', -1);
14346             //this.deferFocus();
14347         }
14348          
14349         if(this.resizable){
14350             this.setSize(this.wrap.getSize());
14351         }
14352         
14353         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14354     },
14355  
14356     // private (for BoxComponent)
14357     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14358
14359     // private (for BoxComponent)
14360     getResizeEl : function(){
14361         return this.wrap;
14362     },
14363
14364     // private (for BoxComponent)
14365     getPositionEl : function(){
14366         return this.wrap;
14367     },
14368
14369     // private
14370     initEvents : function(){
14371         this.originalValue = this.getValue();
14372     },
14373
14374 //    /**
14375 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14376 //     * @method
14377 //     */
14378 //    markInvalid : Roo.emptyFn,
14379 //    /**
14380 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14381 //     * @method
14382 //     */
14383 //    clearInvalid : Roo.emptyFn,
14384
14385     setValue : function(v){
14386         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14387         this.editorcore.pushValue();
14388     },
14389
14390      
14391     // private
14392     deferFocus : function(){
14393         this.focus.defer(10, this);
14394     },
14395
14396     // doc'ed in Field
14397     focus : function(){
14398         this.editorcore.focus();
14399         
14400     },
14401       
14402
14403     // private
14404     onDestroy : function(){
14405         
14406         
14407         
14408         if(this.rendered){
14409             
14410             for (var i =0; i < this.toolbars.length;i++) {
14411                 // fixme - ask toolbars for heights?
14412                 this.toolbars[i].onDestroy();
14413             }
14414             
14415             this.wrap.dom.innerHTML = '';
14416             this.wrap.remove();
14417         }
14418     },
14419
14420     // private
14421     onFirstFocus : function(){
14422         //Roo.log("onFirstFocus");
14423         this.editorcore.onFirstFocus();
14424          for (var i =0; i < this.toolbars.length;i++) {
14425             this.toolbars[i].onFirstFocus();
14426         }
14427         
14428     },
14429     
14430     // private
14431     syncValue : function()
14432     {   
14433         this.editorcore.syncValue();
14434     },
14435     
14436     pushValue : function()
14437     {   
14438         this.editorcore.pushValue();
14439     }
14440      
14441     
14442     // hide stuff that is not compatible
14443     /**
14444      * @event blur
14445      * @hide
14446      */
14447     /**
14448      * @event change
14449      * @hide
14450      */
14451     /**
14452      * @event focus
14453      * @hide
14454      */
14455     /**
14456      * @event specialkey
14457      * @hide
14458      */
14459     /**
14460      * @cfg {String} fieldClass @hide
14461      */
14462     /**
14463      * @cfg {String} focusClass @hide
14464      */
14465     /**
14466      * @cfg {String} autoCreate @hide
14467      */
14468     /**
14469      * @cfg {String} inputType @hide
14470      */
14471     /**
14472      * @cfg {String} invalidClass @hide
14473      */
14474     /**
14475      * @cfg {String} invalidText @hide
14476      */
14477     /**
14478      * @cfg {String} msgFx @hide
14479      */
14480     /**
14481      * @cfg {String} validateOnBlur @hide
14482      */
14483 });
14484  
14485     
14486    
14487    
14488    
14489       
14490
14491 /**
14492  * @class Roo.bootstrap.HtmlEditorToolbar1
14493  * Basic Toolbar
14494  * 
14495  * Usage:
14496  *
14497  new Roo.bootstrap.HtmlEditor({
14498     ....
14499     toolbars : [
14500         new Roo.bootstrap.HtmlEditorToolbar1({
14501             disable : { fonts: 1 , format: 1, ..., ... , ...],
14502             btns : [ .... ]
14503         })
14504     }
14505      
14506  * 
14507  * @cfg {Object} disable List of elements to disable..
14508  * @cfg {Array} btns List of additional buttons.
14509  * 
14510  * 
14511  * NEEDS Extra CSS? 
14512  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14513  */
14514  
14515 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14516 {
14517     
14518     Roo.apply(this, config);
14519     
14520     // default disabled, based on 'good practice'..
14521     this.disable = this.disable || {};
14522     Roo.applyIf(this.disable, {
14523         fontSize : true,
14524         colors : true,
14525         specialElements : true
14526     });
14527     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14528     
14529     this.editor = config.editor;
14530     this.editorcore = config.editor.editorcore;
14531     
14532     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14533     
14534     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14535     // dont call parent... till later.
14536 }
14537 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14538     
14539     
14540     bar : true,
14541     
14542     editor : false,
14543     editorcore : false,
14544     
14545     
14546     formats : [
14547         "p" ,  
14548         "h1","h2","h3","h4","h5","h6", 
14549         "pre", "code", 
14550         "abbr", "acronym", "address", "cite", "samp", "var",
14551         'div','span'
14552     ],
14553     
14554     onRender : function(ct, position)
14555     {
14556        // Roo.log("Call onRender: " + this.xtype);
14557         
14558        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14559        Roo.log(this.el);
14560        this.el.dom.style.marginBottom = '0';
14561        var _this = this;
14562        var editorcore = this.editorcore;
14563        var editor= this.editor;
14564        
14565        var children = [];
14566        var btn = function(id,cmd , toggle, handler){
14567        
14568             var  event = toggle ? 'toggle' : 'click';
14569        
14570             var a = {
14571                 size : 'sm',
14572                 xtype: 'Button',
14573                 xns: Roo.bootstrap,
14574                 glyphicon : id,
14575                 cmd : id || cmd,
14576                 enableToggle:toggle !== false,
14577                 //html : 'submit'
14578                 pressed : toggle ? false : null,
14579                 listeners : {}
14580             }
14581             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14582                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14583             }
14584             children.push(a);
14585             return a;
14586        }
14587         
14588         var style = {
14589                 xtype: 'Button',
14590                 size : 'sm',
14591                 xns: Roo.bootstrap,
14592                 glyphicon : 'font',
14593                 //html : 'submit'
14594                 menu : {
14595                     xtype: 'Menu',
14596                     xns: Roo.bootstrap,
14597                     items:  []
14598                 }
14599         };
14600         Roo.each(this.formats, function(f) {
14601             style.menu.items.push({
14602                 xtype :'MenuItem',
14603                 xns: Roo.bootstrap,
14604                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14605                 tagname : f,
14606                 listeners : {
14607                     click : function()
14608                     {
14609                         editorcore.insertTag(this.tagname);
14610                         editor.focus();
14611                     }
14612                 }
14613                 
14614             });
14615         });
14616          children.push(style);   
14617             
14618             
14619         btn('bold',false,true);
14620         btn('italic',false,true);
14621         btn('align-left', 'justifyleft',true);
14622         btn('align-center', 'justifycenter',true);
14623         btn('align-right' , 'justifyright',true);
14624         btn('link', false, false, function(btn) {
14625             //Roo.log("create link?");
14626             var url = prompt(this.createLinkText, this.defaultLinkValue);
14627             if(url && url != 'http:/'+'/'){
14628                 this.editorcore.relayCmd('createlink', url);
14629             }
14630         }),
14631         btn('list','insertunorderedlist',true);
14632         btn('pencil', false,true, function(btn){
14633                 Roo.log(this);
14634                 
14635                 this.toggleSourceEdit(btn.pressed);
14636         });
14637         /*
14638         var cog = {
14639                 xtype: 'Button',
14640                 size : 'sm',
14641                 xns: Roo.bootstrap,
14642                 glyphicon : 'cog',
14643                 //html : 'submit'
14644                 menu : {
14645                     xtype: 'Menu',
14646                     xns: Roo.bootstrap,
14647                     items:  []
14648                 }
14649         };
14650         
14651         cog.menu.items.push({
14652             xtype :'MenuItem',
14653             xns: Roo.bootstrap,
14654             html : Clean styles,
14655             tagname : f,
14656             listeners : {
14657                 click : function()
14658                 {
14659                     editorcore.insertTag(this.tagname);
14660                     editor.focus();
14661                 }
14662             }
14663             
14664         });
14665        */
14666         
14667          
14668        this.xtype = 'Navbar';
14669         
14670         for(var i=0;i< children.length;i++) {
14671             
14672             this.buttons.add(this.addxtypeChild(children[i]));
14673             
14674         }
14675         
14676         editor.on('editorevent', this.updateToolbar, this);
14677     },
14678     onBtnClick : function(id)
14679     {
14680        this.editorcore.relayCmd(id);
14681        this.editorcore.focus();
14682     },
14683     
14684     /**
14685      * Protected method that will not generally be called directly. It triggers
14686      * a toolbar update by reading the markup state of the current selection in the editor.
14687      */
14688     updateToolbar: function(){
14689
14690         if(!this.editorcore.activated){
14691             this.editor.onFirstFocus(); // is this neeed?
14692             return;
14693         }
14694
14695         var btns = this.buttons; 
14696         var doc = this.editorcore.doc;
14697         btns.get('bold').setActive(doc.queryCommandState('bold'));
14698         btns.get('italic').setActive(doc.queryCommandState('italic'));
14699         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14700         
14701         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14702         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14703         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14704         
14705         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14706         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14707          /*
14708         
14709         var ans = this.editorcore.getAllAncestors();
14710         if (this.formatCombo) {
14711             
14712             
14713             var store = this.formatCombo.store;
14714             this.formatCombo.setValue("");
14715             for (var i =0; i < ans.length;i++) {
14716                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14717                     // select it..
14718                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14719                     break;
14720                 }
14721             }
14722         }
14723         
14724         
14725         
14726         // hides menus... - so this cant be on a menu...
14727         Roo.bootstrap.MenuMgr.hideAll();
14728         */
14729         Roo.bootstrap.MenuMgr.hideAll();
14730         //this.editorsyncValue();
14731     },
14732     onFirstFocus: function() {
14733         this.buttons.each(function(item){
14734            item.enable();
14735         });
14736     },
14737     toggleSourceEdit : function(sourceEditMode){
14738         
14739           
14740         if(sourceEditMode){
14741             Roo.log("disabling buttons");
14742            this.buttons.each( function(item){
14743                 if(item.cmd != 'pencil'){
14744                     item.disable();
14745                 }
14746             });
14747           
14748         }else{
14749             Roo.log("enabling buttons");
14750             if(this.editorcore.initialized){
14751                 this.buttons.each( function(item){
14752                     item.enable();
14753                 });
14754             }
14755             
14756         }
14757         Roo.log("calling toggole on editor");
14758         // tell the editor that it's been pressed..
14759         this.editor.toggleSourceEdit(sourceEditMode);
14760        
14761     }
14762 });
14763
14764
14765
14766
14767
14768 /**
14769  * @class Roo.bootstrap.Table.AbstractSelectionModel
14770  * @extends Roo.util.Observable
14771  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14772  * implemented by descendant classes.  This class should not be directly instantiated.
14773  * @constructor
14774  */
14775 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14776     this.locked = false;
14777     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14778 };
14779
14780
14781 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14782     /** @ignore Called by the grid automatically. Do not call directly. */
14783     init : function(grid){
14784         this.grid = grid;
14785         this.initEvents();
14786     },
14787
14788     /**
14789      * Locks the selections.
14790      */
14791     lock : function(){
14792         this.locked = true;
14793     },
14794
14795     /**
14796      * Unlocks the selections.
14797      */
14798     unlock : function(){
14799         this.locked = false;
14800     },
14801
14802     /**
14803      * Returns true if the selections are locked.
14804      * @return {Boolean}
14805      */
14806     isLocked : function(){
14807         return this.locked;
14808     }
14809 });
14810 /**
14811  * @class Roo.bootstrap.Table.ColumnModel
14812  * @extends Roo.util.Observable
14813  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14814  * the columns in the table.
14815  
14816  * @constructor
14817  * @param {Object} config An Array of column config objects. See this class's
14818  * config objects for details.
14819 */
14820 Roo.bootstrap.Table.ColumnModel = function(config){
14821         /**
14822      * The config passed into the constructor
14823      */
14824     this.config = config;
14825     this.lookup = {};
14826
14827     // if no id, create one
14828     // if the column does not have a dataIndex mapping,
14829     // map it to the order it is in the config
14830     for(var i = 0, len = config.length; i < len; i++){
14831         var c = config[i];
14832         if(typeof c.dataIndex == "undefined"){
14833             c.dataIndex = i;
14834         }
14835         if(typeof c.renderer == "string"){
14836             c.renderer = Roo.util.Format[c.renderer];
14837         }
14838         if(typeof c.id == "undefined"){
14839             c.id = Roo.id();
14840         }
14841 //        if(c.editor && c.editor.xtype){
14842 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14843 //        }
14844 //        if(c.editor && c.editor.isFormField){
14845 //            c.editor = new Roo.grid.GridEditor(c.editor);
14846 //        }
14847
14848         this.lookup[c.id] = c;
14849     }
14850
14851     /**
14852      * The width of columns which have no width specified (defaults to 100)
14853      * @type Number
14854      */
14855     this.defaultWidth = 100;
14856
14857     /**
14858      * Default sortable of columns which have no sortable specified (defaults to false)
14859      * @type Boolean
14860      */
14861     this.defaultSortable = false;
14862
14863     this.addEvents({
14864         /**
14865              * @event widthchange
14866              * Fires when the width of a column changes.
14867              * @param {ColumnModel} this
14868              * @param {Number} columnIndex The column index
14869              * @param {Number} newWidth The new width
14870              */
14871             "widthchange": true,
14872         /**
14873              * @event headerchange
14874              * Fires when the text of a header changes.
14875              * @param {ColumnModel} this
14876              * @param {Number} columnIndex The column index
14877              * @param {Number} newText The new header text
14878              */
14879             "headerchange": true,
14880         /**
14881              * @event hiddenchange
14882              * Fires when a column is hidden or "unhidden".
14883              * @param {ColumnModel} this
14884              * @param {Number} columnIndex The column index
14885              * @param {Boolean} hidden true if hidden, false otherwise
14886              */
14887             "hiddenchange": true,
14888             /**
14889          * @event columnmoved
14890          * Fires when a column is moved.
14891          * @param {ColumnModel} this
14892          * @param {Number} oldIndex
14893          * @param {Number} newIndex
14894          */
14895         "columnmoved" : true,
14896         /**
14897          * @event columlockchange
14898          * Fires when a column's locked state is changed
14899          * @param {ColumnModel} this
14900          * @param {Number} colIndex
14901          * @param {Boolean} locked true if locked
14902          */
14903         "columnlockchange" : true
14904     });
14905     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14906 };
14907 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14908     /**
14909      * @cfg {String} header The header text to display in the Grid view.
14910      */
14911     /**
14912      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14913      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14914      * specified, the column's index is used as an index into the Record's data Array.
14915      */
14916     /**
14917      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14918      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14919      */
14920     /**
14921      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14922      * Defaults to the value of the {@link #defaultSortable} property.
14923      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14924      */
14925     /**
14926      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14927      */
14928     /**
14929      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14930      */
14931     /**
14932      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14933      */
14934     /**
14935      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14936      */
14937     /**
14938      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14939      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14940      * default renderer uses the raw data value.
14941      */
14942     /**
14943      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14944      */
14945
14946     /**
14947      * Returns the id of the column at the specified index.
14948      * @param {Number} index The column index
14949      * @return {String} the id
14950      */
14951     getColumnId : function(index){
14952         return this.config[index].id;
14953     },
14954
14955     /**
14956      * Returns the column for a specified id.
14957      * @param {String} id The column id
14958      * @return {Object} the column
14959      */
14960     getColumnById : function(id){
14961         return this.lookup[id];
14962     },
14963
14964     
14965     /**
14966      * Returns the column for a specified dataIndex.
14967      * @param {String} dataIndex The column dataIndex
14968      * @return {Object|Boolean} the column or false if not found
14969      */
14970     getColumnByDataIndex: function(dataIndex){
14971         var index = this.findColumnIndex(dataIndex);
14972         return index > -1 ? this.config[index] : false;
14973     },
14974     
14975     /**
14976      * Returns the index for a specified column id.
14977      * @param {String} id The column id
14978      * @return {Number} the index, or -1 if not found
14979      */
14980     getIndexById : function(id){
14981         for(var i = 0, len = this.config.length; i < len; i++){
14982             if(this.config[i].id == id){
14983                 return i;
14984             }
14985         }
14986         return -1;
14987     },
14988     
14989     /**
14990      * Returns the index for a specified column dataIndex.
14991      * @param {String} dataIndex The column dataIndex
14992      * @return {Number} the index, or -1 if not found
14993      */
14994     
14995     findColumnIndex : function(dataIndex){
14996         for(var i = 0, len = this.config.length; i < len; i++){
14997             if(this.config[i].dataIndex == dataIndex){
14998                 return i;
14999             }
15000         }
15001         return -1;
15002     },
15003     
15004     
15005     moveColumn : function(oldIndex, newIndex){
15006         var c = this.config[oldIndex];
15007         this.config.splice(oldIndex, 1);
15008         this.config.splice(newIndex, 0, c);
15009         this.dataMap = null;
15010         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15011     },
15012
15013     isLocked : function(colIndex){
15014         return this.config[colIndex].locked === true;
15015     },
15016
15017     setLocked : function(colIndex, value, suppressEvent){
15018         if(this.isLocked(colIndex) == value){
15019             return;
15020         }
15021         this.config[colIndex].locked = value;
15022         if(!suppressEvent){
15023             this.fireEvent("columnlockchange", this, colIndex, value);
15024         }
15025     },
15026
15027     getTotalLockedWidth : function(){
15028         var totalWidth = 0;
15029         for(var i = 0; i < this.config.length; i++){
15030             if(this.isLocked(i) && !this.isHidden(i)){
15031                 this.totalWidth += this.getColumnWidth(i);
15032             }
15033         }
15034         return totalWidth;
15035     },
15036
15037     getLockedCount : function(){
15038         for(var i = 0, len = this.config.length; i < len; i++){
15039             if(!this.isLocked(i)){
15040                 return i;
15041             }
15042         }
15043     },
15044
15045     /**
15046      * Returns the number of columns.
15047      * @return {Number}
15048      */
15049     getColumnCount : function(visibleOnly){
15050         if(visibleOnly === true){
15051             var c = 0;
15052             for(var i = 0, len = this.config.length; i < len; i++){
15053                 if(!this.isHidden(i)){
15054                     c++;
15055                 }
15056             }
15057             return c;
15058         }
15059         return this.config.length;
15060     },
15061
15062     /**
15063      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15064      * @param {Function} fn
15065      * @param {Object} scope (optional)
15066      * @return {Array} result
15067      */
15068     getColumnsBy : function(fn, scope){
15069         var r = [];
15070         for(var i = 0, len = this.config.length; i < len; i++){
15071             var c = this.config[i];
15072             if(fn.call(scope||this, c, i) === true){
15073                 r[r.length] = c;
15074             }
15075         }
15076         return r;
15077     },
15078
15079     /**
15080      * Returns true if the specified column is sortable.
15081      * @param {Number} col The column index
15082      * @return {Boolean}
15083      */
15084     isSortable : function(col){
15085         if(typeof this.config[col].sortable == "undefined"){
15086             return this.defaultSortable;
15087         }
15088         return this.config[col].sortable;
15089     },
15090
15091     /**
15092      * Returns the rendering (formatting) function defined for the column.
15093      * @param {Number} col The column index.
15094      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15095      */
15096     getRenderer : function(col){
15097         if(!this.config[col].renderer){
15098             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15099         }
15100         return this.config[col].renderer;
15101     },
15102
15103     /**
15104      * Sets the rendering (formatting) function for a column.
15105      * @param {Number} col The column index
15106      * @param {Function} fn The function to use to process the cell's raw data
15107      * to return HTML markup for the grid view. The render function is called with
15108      * the following parameters:<ul>
15109      * <li>Data value.</li>
15110      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15111      * <li>css A CSS style string to apply to the table cell.</li>
15112      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15113      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15114      * <li>Row index</li>
15115      * <li>Column index</li>
15116      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15117      */
15118     setRenderer : function(col, fn){
15119         this.config[col].renderer = fn;
15120     },
15121
15122     /**
15123      * Returns the width for the specified column.
15124      * @param {Number} col The column index
15125      * @return {Number}
15126      */
15127     getColumnWidth : function(col){
15128         return this.config[col].width * 1 || this.defaultWidth;
15129     },
15130
15131     /**
15132      * Sets the width for a column.
15133      * @param {Number} col The column index
15134      * @param {Number} width The new width
15135      */
15136     setColumnWidth : function(col, width, suppressEvent){
15137         this.config[col].width = width;
15138         this.totalWidth = null;
15139         if(!suppressEvent){
15140              this.fireEvent("widthchange", this, col, width);
15141         }
15142     },
15143
15144     /**
15145      * Returns the total width of all columns.
15146      * @param {Boolean} includeHidden True to include hidden column widths
15147      * @return {Number}
15148      */
15149     getTotalWidth : function(includeHidden){
15150         if(!this.totalWidth){
15151             this.totalWidth = 0;
15152             for(var i = 0, len = this.config.length; i < len; i++){
15153                 if(includeHidden || !this.isHidden(i)){
15154                     this.totalWidth += this.getColumnWidth(i);
15155                 }
15156             }
15157         }
15158         return this.totalWidth;
15159     },
15160
15161     /**
15162      * Returns the header for the specified column.
15163      * @param {Number} col The column index
15164      * @return {String}
15165      */
15166     getColumnHeader : function(col){
15167         return this.config[col].header;
15168     },
15169
15170     /**
15171      * Sets the header for a column.
15172      * @param {Number} col The column index
15173      * @param {String} header The new header
15174      */
15175     setColumnHeader : function(col, header){
15176         this.config[col].header = header;
15177         this.fireEvent("headerchange", this, col, header);
15178     },
15179
15180     /**
15181      * Returns the tooltip for the specified column.
15182      * @param {Number} col The column index
15183      * @return {String}
15184      */
15185     getColumnTooltip : function(col){
15186             return this.config[col].tooltip;
15187     },
15188     /**
15189      * Sets the tooltip for a column.
15190      * @param {Number} col The column index
15191      * @param {String} tooltip The new tooltip
15192      */
15193     setColumnTooltip : function(col, tooltip){
15194             this.config[col].tooltip = tooltip;
15195     },
15196
15197     /**
15198      * Returns the dataIndex for the specified column.
15199      * @param {Number} col The column index
15200      * @return {Number}
15201      */
15202     getDataIndex : function(col){
15203         return this.config[col].dataIndex;
15204     },
15205
15206     /**
15207      * Sets the dataIndex for a column.
15208      * @param {Number} col The column index
15209      * @param {Number} dataIndex The new dataIndex
15210      */
15211     setDataIndex : function(col, dataIndex){
15212         this.config[col].dataIndex = dataIndex;
15213     },
15214
15215     
15216     
15217     /**
15218      * Returns true if the cell is editable.
15219      * @param {Number} colIndex The column index
15220      * @param {Number} rowIndex The row index
15221      * @return {Boolean}
15222      */
15223     isCellEditable : function(colIndex, rowIndex){
15224         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15225     },
15226
15227     /**
15228      * Returns the editor defined for the cell/column.
15229      * return false or null to disable editing.
15230      * @param {Number} colIndex The column index
15231      * @param {Number} rowIndex The row index
15232      * @return {Object}
15233      */
15234     getCellEditor : function(colIndex, rowIndex){
15235         return this.config[colIndex].editor;
15236     },
15237
15238     /**
15239      * Sets if a column is editable.
15240      * @param {Number} col The column index
15241      * @param {Boolean} editable True if the column is editable
15242      */
15243     setEditable : function(col, editable){
15244         this.config[col].editable = editable;
15245     },
15246
15247
15248     /**
15249      * Returns true if the column is hidden.
15250      * @param {Number} colIndex The column index
15251      * @return {Boolean}
15252      */
15253     isHidden : function(colIndex){
15254         return this.config[colIndex].hidden;
15255     },
15256
15257
15258     /**
15259      * Returns true if the column width cannot be changed
15260      */
15261     isFixed : function(colIndex){
15262         return this.config[colIndex].fixed;
15263     },
15264
15265     /**
15266      * Returns true if the column can be resized
15267      * @return {Boolean}
15268      */
15269     isResizable : function(colIndex){
15270         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15271     },
15272     /**
15273      * Sets if a column is hidden.
15274      * @param {Number} colIndex The column index
15275      * @param {Boolean} hidden True if the column is hidden
15276      */
15277     setHidden : function(colIndex, hidden){
15278         this.config[colIndex].hidden = hidden;
15279         this.totalWidth = null;
15280         this.fireEvent("hiddenchange", this, colIndex, hidden);
15281     },
15282
15283     /**
15284      * Sets the editor for a column.
15285      * @param {Number} col The column index
15286      * @param {Object} editor The editor object
15287      */
15288     setEditor : function(col, editor){
15289         this.config[col].editor = editor;
15290     }
15291 });
15292
15293 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15294         if(typeof value == "string" && value.length < 1){
15295             return "&#160;";
15296         }
15297         return value;
15298 };
15299
15300 // Alias for backwards compatibility
15301 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15302
15303 /**
15304  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15305  * @class Roo.bootstrap.Table.RowSelectionModel
15306  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15307  * It supports multiple selections and keyboard selection/navigation. 
15308  * @constructor
15309  * @param {Object} config
15310  */
15311
15312 Roo.bootstrap.Table.RowSelectionModel = function(config){
15313     Roo.apply(this, config);
15314     this.selections = new Roo.util.MixedCollection(false, function(o){
15315         return o.id;
15316     });
15317
15318     this.last = false;
15319     this.lastActive = false;
15320
15321     this.addEvents({
15322         /**
15323              * @event selectionchange
15324              * Fires when the selection changes
15325              * @param {SelectionModel} this
15326              */
15327             "selectionchange" : true,
15328         /**
15329              * @event afterselectionchange
15330              * Fires after the selection changes (eg. by key press or clicking)
15331              * @param {SelectionModel} this
15332              */
15333             "afterselectionchange" : true,
15334         /**
15335              * @event beforerowselect
15336              * Fires when a row is selected being selected, return false to cancel.
15337              * @param {SelectionModel} this
15338              * @param {Number} rowIndex The selected index
15339              * @param {Boolean} keepExisting False if other selections will be cleared
15340              */
15341             "beforerowselect" : true,
15342         /**
15343              * @event rowselect
15344              * Fires when a row is selected.
15345              * @param {SelectionModel} this
15346              * @param {Number} rowIndex The selected index
15347              * @param {Roo.data.Record} r The record
15348              */
15349             "rowselect" : true,
15350         /**
15351              * @event rowdeselect
15352              * Fires when a row is deselected.
15353              * @param {SelectionModel} this
15354              * @param {Number} rowIndex The selected index
15355              */
15356         "rowdeselect" : true
15357     });
15358     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15359     this.locked = false;
15360 };
15361
15362 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15363     /**
15364      * @cfg {Boolean} singleSelect
15365      * True to allow selection of only one row at a time (defaults to false)
15366      */
15367     singleSelect : false,
15368
15369     // private
15370     initEvents : function(){
15371
15372         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15373             this.grid.on("mousedown", this.handleMouseDown, this);
15374         }else{ // allow click to work like normal
15375             this.grid.on("rowclick", this.handleDragableRowClick, this);
15376         }
15377
15378         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15379             "up" : function(e){
15380                 if(!e.shiftKey){
15381                     this.selectPrevious(e.shiftKey);
15382                 }else if(this.last !== false && this.lastActive !== false){
15383                     var last = this.last;
15384                     this.selectRange(this.last,  this.lastActive-1);
15385                     this.grid.getView().focusRow(this.lastActive);
15386                     if(last !== false){
15387                         this.last = last;
15388                     }
15389                 }else{
15390                     this.selectFirstRow();
15391                 }
15392                 this.fireEvent("afterselectionchange", this);
15393             },
15394             "down" : function(e){
15395                 if(!e.shiftKey){
15396                     this.selectNext(e.shiftKey);
15397                 }else if(this.last !== false && this.lastActive !== false){
15398                     var last = this.last;
15399                     this.selectRange(this.last,  this.lastActive+1);
15400                     this.grid.getView().focusRow(this.lastActive);
15401                     if(last !== false){
15402                         this.last = last;
15403                     }
15404                 }else{
15405                     this.selectFirstRow();
15406                 }
15407                 this.fireEvent("afterselectionchange", this);
15408             },
15409             scope: this
15410         });
15411
15412         var view = this.grid.view;
15413         view.on("refresh", this.onRefresh, this);
15414         view.on("rowupdated", this.onRowUpdated, this);
15415         view.on("rowremoved", this.onRemove, this);
15416     },
15417
15418     // private
15419     onRefresh : function(){
15420         var ds = this.grid.dataSource, i, v = this.grid.view;
15421         var s = this.selections;
15422         s.each(function(r){
15423             if((i = ds.indexOfId(r.id)) != -1){
15424                 v.onRowSelect(i);
15425             }else{
15426                 s.remove(r);
15427             }
15428         });
15429     },
15430
15431     // private
15432     onRemove : function(v, index, r){
15433         this.selections.remove(r);
15434     },
15435
15436     // private
15437     onRowUpdated : function(v, index, r){
15438         if(this.isSelected(r)){
15439             v.onRowSelect(index);
15440         }
15441     },
15442
15443     /**
15444      * Select records.
15445      * @param {Array} records The records to select
15446      * @param {Boolean} keepExisting (optional) True to keep existing selections
15447      */
15448     selectRecords : function(records, keepExisting){
15449         if(!keepExisting){
15450             this.clearSelections();
15451         }
15452         var ds = this.grid.dataSource;
15453         for(var i = 0, len = records.length; i < len; i++){
15454             this.selectRow(ds.indexOf(records[i]), true);
15455         }
15456     },
15457
15458     /**
15459      * Gets the number of selected rows.
15460      * @return {Number}
15461      */
15462     getCount : function(){
15463         return this.selections.length;
15464     },
15465
15466     /**
15467      * Selects the first row in the grid.
15468      */
15469     selectFirstRow : function(){
15470         this.selectRow(0);
15471     },
15472
15473     /**
15474      * Select the last row.
15475      * @param {Boolean} keepExisting (optional) True to keep existing selections
15476      */
15477     selectLastRow : function(keepExisting){
15478         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15479     },
15480
15481     /**
15482      * Selects the row immediately following the last selected row.
15483      * @param {Boolean} keepExisting (optional) True to keep existing selections
15484      */
15485     selectNext : function(keepExisting){
15486         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15487             this.selectRow(this.last+1, keepExisting);
15488             this.grid.getView().focusRow(this.last);
15489         }
15490     },
15491
15492     /**
15493      * Selects the row that precedes the last selected row.
15494      * @param {Boolean} keepExisting (optional) True to keep existing selections
15495      */
15496     selectPrevious : function(keepExisting){
15497         if(this.last){
15498             this.selectRow(this.last-1, keepExisting);
15499             this.grid.getView().focusRow(this.last);
15500         }
15501     },
15502
15503     /**
15504      * Returns the selected records
15505      * @return {Array} Array of selected records
15506      */
15507     getSelections : function(){
15508         return [].concat(this.selections.items);
15509     },
15510
15511     /**
15512      * Returns the first selected record.
15513      * @return {Record}
15514      */
15515     getSelected : function(){
15516         return this.selections.itemAt(0);
15517     },
15518
15519
15520     /**
15521      * Clears all selections.
15522      */
15523     clearSelections : function(fast){
15524         if(this.locked) return;
15525         if(fast !== true){
15526             var ds = this.grid.dataSource;
15527             var s = this.selections;
15528             s.each(function(r){
15529                 this.deselectRow(ds.indexOfId(r.id));
15530             }, this);
15531             s.clear();
15532         }else{
15533             this.selections.clear();
15534         }
15535         this.last = false;
15536     },
15537
15538
15539     /**
15540      * Selects all rows.
15541      */
15542     selectAll : function(){
15543         if(this.locked) return;
15544         this.selections.clear();
15545         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15546             this.selectRow(i, true);
15547         }
15548     },
15549
15550     /**
15551      * Returns True if there is a selection.
15552      * @return {Boolean}
15553      */
15554     hasSelection : function(){
15555         return this.selections.length > 0;
15556     },
15557
15558     /**
15559      * Returns True if the specified row is selected.
15560      * @param {Number/Record} record The record or index of the record to check
15561      * @return {Boolean}
15562      */
15563     isSelected : function(index){
15564         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15565         return (r && this.selections.key(r.id) ? true : false);
15566     },
15567
15568     /**
15569      * Returns True if the specified record id is selected.
15570      * @param {String} id The id of record to check
15571      * @return {Boolean}
15572      */
15573     isIdSelected : function(id){
15574         return (this.selections.key(id) ? true : false);
15575     },
15576
15577     // private
15578     handleMouseDown : function(e, t){
15579         var view = this.grid.getView(), rowIndex;
15580         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15581             return;
15582         };
15583         if(e.shiftKey && this.last !== false){
15584             var last = this.last;
15585             this.selectRange(last, rowIndex, e.ctrlKey);
15586             this.last = last; // reset the last
15587             view.focusRow(rowIndex);
15588         }else{
15589             var isSelected = this.isSelected(rowIndex);
15590             if(e.button !== 0 && isSelected){
15591                 view.focusRow(rowIndex);
15592             }else if(e.ctrlKey && isSelected){
15593                 this.deselectRow(rowIndex);
15594             }else if(!isSelected){
15595                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15596                 view.focusRow(rowIndex);
15597             }
15598         }
15599         this.fireEvent("afterselectionchange", this);
15600     },
15601     // private
15602     handleDragableRowClick :  function(grid, rowIndex, e) 
15603     {
15604         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15605             this.selectRow(rowIndex, false);
15606             grid.view.focusRow(rowIndex);
15607              this.fireEvent("afterselectionchange", this);
15608         }
15609     },
15610     
15611     /**
15612      * Selects multiple rows.
15613      * @param {Array} rows Array of the indexes of the row to select
15614      * @param {Boolean} keepExisting (optional) True to keep existing selections
15615      */
15616     selectRows : function(rows, keepExisting){
15617         if(!keepExisting){
15618             this.clearSelections();
15619         }
15620         for(var i = 0, len = rows.length; i < len; i++){
15621             this.selectRow(rows[i], true);
15622         }
15623     },
15624
15625     /**
15626      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15627      * @param {Number} startRow The index of the first row in the range
15628      * @param {Number} endRow The index of the last row in the range
15629      * @param {Boolean} keepExisting (optional) True to retain existing selections
15630      */
15631     selectRange : function(startRow, endRow, keepExisting){
15632         if(this.locked) return;
15633         if(!keepExisting){
15634             this.clearSelections();
15635         }
15636         if(startRow <= endRow){
15637             for(var i = startRow; i <= endRow; i++){
15638                 this.selectRow(i, true);
15639             }
15640         }else{
15641             for(var i = startRow; i >= endRow; i--){
15642                 this.selectRow(i, true);
15643             }
15644         }
15645     },
15646
15647     /**
15648      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15649      * @param {Number} startRow The index of the first row in the range
15650      * @param {Number} endRow The index of the last row in the range
15651      */
15652     deselectRange : function(startRow, endRow, preventViewNotify){
15653         if(this.locked) return;
15654         for(var i = startRow; i <= endRow; i++){
15655             this.deselectRow(i, preventViewNotify);
15656         }
15657     },
15658
15659     /**
15660      * Selects a row.
15661      * @param {Number} row The index of the row to select
15662      * @param {Boolean} keepExisting (optional) True to keep existing selections
15663      */
15664     selectRow : function(index, keepExisting, preventViewNotify){
15665         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15666         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15667             if(!keepExisting || this.singleSelect){
15668                 this.clearSelections();
15669             }
15670             var r = this.grid.dataSource.getAt(index);
15671             this.selections.add(r);
15672             this.last = this.lastActive = index;
15673             if(!preventViewNotify){
15674                 this.grid.getView().onRowSelect(index);
15675             }
15676             this.fireEvent("rowselect", this, index, r);
15677             this.fireEvent("selectionchange", this);
15678         }
15679     },
15680
15681     /**
15682      * Deselects a row.
15683      * @param {Number} row The index of the row to deselect
15684      */
15685     deselectRow : function(index, preventViewNotify){
15686         if(this.locked) return;
15687         if(this.last == index){
15688             this.last = false;
15689         }
15690         if(this.lastActive == index){
15691             this.lastActive = false;
15692         }
15693         var r = this.grid.dataSource.getAt(index);
15694         this.selections.remove(r);
15695         if(!preventViewNotify){
15696             this.grid.getView().onRowDeselect(index);
15697         }
15698         this.fireEvent("rowdeselect", this, index);
15699         this.fireEvent("selectionchange", this);
15700     },
15701
15702     // private
15703     restoreLast : function(){
15704         if(this._last){
15705             this.last = this._last;
15706         }
15707     },
15708
15709     // private
15710     acceptsNav : function(row, col, cm){
15711         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15712     },
15713
15714     // private
15715     onEditorKey : function(field, e){
15716         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15717         if(k == e.TAB){
15718             e.stopEvent();
15719             ed.completeEdit();
15720             if(e.shiftKey){
15721                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15722             }else{
15723                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15724             }
15725         }else if(k == e.ENTER && !e.ctrlKey){
15726             e.stopEvent();
15727             ed.completeEdit();
15728             if(e.shiftKey){
15729                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15730             }else{
15731                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15732             }
15733         }else if(k == e.ESC){
15734             ed.cancelEdit();
15735         }
15736         if(newCell){
15737             g.startEditing(newCell[0], newCell[1]);
15738         }
15739     }
15740 });/*
15741  * - LGPL
15742  *
15743  * element
15744  * 
15745  */
15746
15747 /**
15748  * @class Roo.bootstrap.MessageBar
15749  * @extends Roo.bootstrap.Component
15750  * Bootstrap MessageBar class
15751  * @cfg {String} html contents of the MessageBar
15752  * @cfg {String} weight (info | success | warning | danger) default info
15753  * @cfg {String} beforeClass insert the bar before the given class
15754  * @cfg {Boolean} closable (true | false) default false
15755  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15756  * 
15757  * @constructor
15758  * Create a new Element
15759  * @param {Object} config The config object
15760  */
15761
15762 Roo.bootstrap.MessageBar = function(config){
15763     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15764 };
15765
15766 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15767     
15768     html: '',
15769     weight: 'info',
15770     closable: false,
15771     fixed: false,
15772     beforeClass: 'bootstrap-sticky-wrap',
15773     
15774     getAutoCreate : function(){
15775         
15776         var cfg = {
15777             tag: 'div',
15778             cls: 'alert alert-dismissable alert-' + this.weight,
15779             cn: [
15780                 {
15781                     tag: 'span',
15782                     cls: 'message',
15783                     html: this.html || ''
15784                 }
15785             ]
15786         }
15787         
15788         if(this.fixed){
15789             cfg.cls += ' alert-messages-fixed';
15790         }
15791         
15792         if(this.closable){
15793             cfg.cn.push({
15794                 tag: 'button',
15795                 cls: 'close',
15796                 html: 'x'
15797             });
15798         }
15799         
15800         return cfg;
15801     },
15802     
15803     onRender : function(ct, position)
15804     {
15805         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15806         
15807         if(!this.el){
15808             var cfg = Roo.apply({},  this.getAutoCreate());
15809             cfg.id = Roo.id();
15810             
15811             if (this.cls) {
15812                 cfg.cls += ' ' + this.cls;
15813             }
15814             if (this.style) {
15815                 cfg.style = this.style;
15816             }
15817             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15818             
15819             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15820         }
15821         
15822         this.el.select('>button.close').on('click', this.hide, this);
15823         
15824     },
15825     
15826     show : function()
15827     {
15828         if (!this.rendered) {
15829             this.render();
15830         }
15831         
15832         this.el.show();
15833         
15834         this.fireEvent('show', this);
15835         
15836     },
15837     
15838     hide : function()
15839     {
15840         if (!this.rendered) {
15841             this.render();
15842         }
15843         
15844         this.el.hide();
15845         
15846         this.fireEvent('hide', this);
15847     },
15848     
15849     update : function()
15850     {
15851 //        var e = this.el.dom.firstChild;
15852 //        
15853 //        if(this.closable){
15854 //            e = e.nextSibling;
15855 //        }
15856 //        
15857 //        e.data = this.html || '';
15858
15859         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15860     }
15861    
15862 });
15863
15864  
15865
15866