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         var _this = this;
2954         
2955         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
2956             e.on('click', _this.sort, _this);
2957         });
2958 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2959 //        this.maskEl.enableDisplayMode("block");
2960 //        this.maskEl.show();
2961         
2962         this.store.on('load', this.onLoad, this);
2963         this.store.on('beforeload', this.onBeforeLoad, this);
2964         
2965         this.store.load();
2966         
2967         
2968         
2969     },
2970     
2971     sort : function(e,el)
2972     {
2973         var sort = Roo.get(el).attr('sort');
2974         Roo.log(sort);
2975         //this.store.sortInfo = {field:'created_dt',direction:'DESC'};
2976         
2977         //this.store.load();
2978     },
2979     
2980     renderHeader : function()
2981     {
2982         var header = {
2983             tag: 'thead',
2984             cn : []
2985         };
2986         
2987         var cm = this.cm;
2988         
2989         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2990             
2991             var config = cm.config[i];
2992             
2993             var c = {
2994                 tag: 'th',
2995                 html: cm.getColumnHeader(i)
2996             };
2997             
2998             if(typeof(config.dataIndex) != 'undefined'){
2999                 c.sort = config.dataIndex;
3000             }
3001             
3002             if(typeof(config.sortable) != 'undefined' && config.sortable){
3003                 c.cls = 'sortable';
3004             }
3005             
3006             header.cn.push(c)
3007         }
3008         
3009         return header;
3010     },
3011     
3012     renderBody : function()
3013     {
3014         var body = {
3015             tag: 'tbody',
3016             cn : []
3017         };
3018         
3019         return body;
3020     },
3021     
3022     renderFooter : function()
3023     {
3024         var footer = {
3025             tag: 'tfoot',
3026             cn : []
3027         };
3028         
3029         return footer;
3030     },
3031     
3032     onLoad : function()
3033     {
3034         Roo.log('ds onload');
3035         
3036         var cm = this.cm;
3037         
3038         var tbody = this.el.select('tbody', true).first();
3039         
3040         var renders = [];
3041         
3042         if(this.store.getCount() > 0){
3043             this.store.data.each(function(d){
3044                 var row = {
3045                     tag : 'tr',
3046                     cn : []
3047                 };
3048                 
3049                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3050                     var renderer = cm.getRenderer(i);
3051                     var config = cm.config[i];
3052                     var value = '';
3053                     var id = Roo.id();
3054                     
3055                     if(typeof(renderer) !== 'undefined'){
3056                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3057                     }
3058                     
3059                     if(typeof(value) === 'object'){
3060                         renders.push({
3061                             id : id,
3062                             cfg : value 
3063                         })
3064                     }
3065                     
3066                     var td = {
3067                         tag: 'td',
3068                         id: id,
3069                         html: (typeof(value) === 'object') ? '' : value
3070                     };
3071                     
3072                     if(typeof(config.width) != 'undefined'){
3073                         td.width = config.width;
3074                     }
3075                     
3076                     row.cn.push(td);
3077                    
3078                 }
3079                 
3080                 tbody.createChild(row);
3081                 
3082             });
3083         }
3084         
3085         
3086         if(renders.length){
3087             var _this = this;
3088             Roo.each(renders, function(r){
3089                 _this.renderColumn(r);
3090             })
3091         }
3092 //        
3093 //        if(this.loadMask){
3094 //            this.maskEl.hide();
3095 //        }
3096     },
3097     
3098     onBeforeLoad : function()
3099     {
3100         Roo.log('ds onBeforeLoad');
3101         
3102         this.clear();
3103         
3104 //        if(this.loadMask){
3105 //            this.maskEl.show();
3106 //        }
3107     },
3108     
3109     clear : function()
3110     {
3111         this.el.select('tbody', true).first().dom.innerHTML = '';
3112     },
3113     
3114     getSelectionModel : function(){
3115         if(!this.selModel){
3116             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3117         }
3118         return this.selModel;
3119     },
3120     
3121     renderColumn : function(r)
3122     {
3123         var _this = this;
3124         r.cfg.render(Roo.get(r.id));
3125         
3126         if(r.cfg.cn){
3127             Roo.each(r.cfg.cn, function(c){
3128                 var child = {
3129                     id: r.id,
3130                     cfg: c
3131                 }
3132                 _this.renderColumn(child);
3133             })
3134         }
3135     }
3136    
3137 });
3138
3139  
3140
3141  /*
3142  * - LGPL
3143  *
3144  * table cell
3145  * 
3146  */
3147
3148 /**
3149  * @class Roo.bootstrap.TableCell
3150  * @extends Roo.bootstrap.Component
3151  * Bootstrap TableCell class
3152  * @cfg {String} html cell contain text
3153  * @cfg {String} cls cell class
3154  * @cfg {String} tag cell tag (td|th) default td
3155  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3156  * @cfg {String} align Aligns the content in a cell
3157  * @cfg {String} axis Categorizes cells
3158  * @cfg {String} bgcolor Specifies the background color of a cell
3159  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3160  * @cfg {Number} colspan Specifies the number of columns a cell should span
3161  * @cfg {String} headers Specifies one or more header cells a cell is related to
3162  * @cfg {Number} height Sets the height of a cell
3163  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3164  * @cfg {Number} rowspan Sets the number of rows a cell should span
3165  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3166  * @cfg {String} valign Vertical aligns the content in a cell
3167  * @cfg {Number} width Specifies the width of a cell
3168  * 
3169  * @constructor
3170  * Create a new TableCell
3171  * @param {Object} config The config object
3172  */
3173
3174 Roo.bootstrap.TableCell = function(config){
3175     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3176 };
3177
3178 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3179     
3180     html: false,
3181     cls: false,
3182     tag: false,
3183     abbr: false,
3184     align: false,
3185     axis: false,
3186     bgcolor: false,
3187     charoff: false,
3188     colspan: false,
3189     headers: false,
3190     height: false,
3191     nowrap: false,
3192     rowspan: false,
3193     scope: false,
3194     valign: false,
3195     width: false,
3196     
3197     
3198     getAutoCreate : function(){
3199         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3200         
3201         cfg = {
3202             tag: 'td'
3203         }
3204         
3205         if(this.tag){
3206             cfg.tag = this.tag;
3207         }
3208         
3209         if (this.html) {
3210             cfg.html=this.html
3211         }
3212         if (this.cls) {
3213             cfg.cls=this.cls
3214         }
3215         if (this.abbr) {
3216             cfg.abbr=this.abbr
3217         }
3218         if (this.align) {
3219             cfg.align=this.align
3220         }
3221         if (this.axis) {
3222             cfg.axis=this.axis
3223         }
3224         if (this.bgcolor) {
3225             cfg.bgcolor=this.bgcolor
3226         }
3227         if (this.charoff) {
3228             cfg.charoff=this.charoff
3229         }
3230         if (this.colspan) {
3231             cfg.colspan=this.colspan
3232         }
3233         if (this.headers) {
3234             cfg.headers=this.headers
3235         }
3236         if (this.height) {
3237             cfg.height=this.height
3238         }
3239         if (this.nowrap) {
3240             cfg.nowrap=this.nowrap
3241         }
3242         if (this.rowspan) {
3243             cfg.rowspan=this.rowspan
3244         }
3245         if (this.scope) {
3246             cfg.scope=this.scope
3247         }
3248         if (this.valign) {
3249             cfg.valign=this.valign
3250         }
3251         if (this.width) {
3252             cfg.width=this.width
3253         }
3254         
3255         
3256         return cfg;
3257     }
3258    
3259 });
3260
3261  
3262
3263  /*
3264  * - LGPL
3265  *
3266  * table row
3267  * 
3268  */
3269
3270 /**
3271  * @class Roo.bootstrap.TableRow
3272  * @extends Roo.bootstrap.Component
3273  * Bootstrap TableRow class
3274  * @cfg {String} cls row class
3275  * @cfg {String} align Aligns the content in a table row
3276  * @cfg {String} bgcolor Specifies a background color for a table row
3277  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3278  * @cfg {String} valign Vertical aligns the content in a table row
3279  * 
3280  * @constructor
3281  * Create a new TableRow
3282  * @param {Object} config The config object
3283  */
3284
3285 Roo.bootstrap.TableRow = function(config){
3286     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3287 };
3288
3289 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3290     
3291     cls: false,
3292     align: false,
3293     bgcolor: false,
3294     charoff: false,
3295     valign: false,
3296     
3297     getAutoCreate : function(){
3298         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3299         
3300         cfg = {
3301             tag: 'tr'
3302         }
3303             
3304         if(this.cls){
3305             cfg.cls = this.cls;
3306         }
3307         if(this.align){
3308             cfg.align = this.align;
3309         }
3310         if(this.bgcolor){
3311             cfg.bgcolor = this.bgcolor;
3312         }
3313         if(this.charoff){
3314             cfg.charoff = this.charoff;
3315         }
3316         if(this.valign){
3317             cfg.valign = this.valign;
3318         }
3319         
3320         return cfg;
3321     }
3322    
3323 });
3324
3325  
3326
3327  /*
3328  * - LGPL
3329  *
3330  * table body
3331  * 
3332  */
3333
3334 /**
3335  * @class Roo.bootstrap.TableBody
3336  * @extends Roo.bootstrap.Component
3337  * Bootstrap TableBody class
3338  * @cfg {String} cls element class
3339  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3340  * @cfg {String} align Aligns the content inside the element
3341  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3342  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3343  * 
3344  * @constructor
3345  * Create a new TableBody
3346  * @param {Object} config The config object
3347  */
3348
3349 Roo.bootstrap.TableBody = function(config){
3350     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3351 };
3352
3353 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3354     
3355     cls: false,
3356     tag: false,
3357     align: false,
3358     charoff: false,
3359     valign: false,
3360     
3361     getAutoCreate : function(){
3362         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3363         
3364         cfg = {
3365             tag: 'tbody'
3366         }
3367             
3368         if (this.cls) {
3369             cfg.cls=this.cls
3370         }
3371         if(this.tag){
3372             cfg.tag = this.tag;
3373         }
3374         
3375         if(this.align){
3376             cfg.align = this.align;
3377         }
3378         if(this.charoff){
3379             cfg.charoff = this.charoff;
3380         }
3381         if(this.valign){
3382             cfg.valign = this.valign;
3383         }
3384         
3385         return cfg;
3386     }
3387     
3388     
3389 //    initEvents : function()
3390 //    {
3391 //        
3392 //        if(!this.store){
3393 //            return;
3394 //        }
3395 //        
3396 //        this.store = Roo.factory(this.store, Roo.data);
3397 //        this.store.on('load', this.onLoad, this);
3398 //        
3399 //        this.store.load();
3400 //        
3401 //    },
3402 //    
3403 //    onLoad: function () 
3404 //    {   
3405 //        this.fireEvent('load', this);
3406 //    }
3407 //    
3408 //   
3409 });
3410
3411  
3412
3413  /*
3414  * Based on:
3415  * Ext JS Library 1.1.1
3416  * Copyright(c) 2006-2007, Ext JS, LLC.
3417  *
3418  * Originally Released Under LGPL - original licence link has changed is not relivant.
3419  *
3420  * Fork - LGPL
3421  * <script type="text/javascript">
3422  */
3423
3424 // as we use this in bootstrap.
3425 Roo.namespace('Roo.form');
3426  /**
3427  * @class Roo.form.Action
3428  * Internal Class used to handle form actions
3429  * @constructor
3430  * @param {Roo.form.BasicForm} el The form element or its id
3431  * @param {Object} config Configuration options
3432  */
3433
3434  
3435  
3436 // define the action interface
3437 Roo.form.Action = function(form, options){
3438     this.form = form;
3439     this.options = options || {};
3440 };
3441 /**
3442  * Client Validation Failed
3443  * @const 
3444  */
3445 Roo.form.Action.CLIENT_INVALID = 'client';
3446 /**
3447  * Server Validation Failed
3448  * @const 
3449  */
3450 Roo.form.Action.SERVER_INVALID = 'server';
3451  /**
3452  * Connect to Server Failed
3453  * @const 
3454  */
3455 Roo.form.Action.CONNECT_FAILURE = 'connect';
3456 /**
3457  * Reading Data from Server Failed
3458  * @const 
3459  */
3460 Roo.form.Action.LOAD_FAILURE = 'load';
3461
3462 Roo.form.Action.prototype = {
3463     type : 'default',
3464     failureType : undefined,
3465     response : undefined,
3466     result : undefined,
3467
3468     // interface method
3469     run : function(options){
3470
3471     },
3472
3473     // interface method
3474     success : function(response){
3475
3476     },
3477
3478     // interface method
3479     handleResponse : function(response){
3480
3481     },
3482
3483     // default connection failure
3484     failure : function(response){
3485         
3486         this.response = response;
3487         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3488         this.form.afterAction(this, false);
3489     },
3490
3491     processResponse : function(response){
3492         this.response = response;
3493         if(!response.responseText){
3494             return true;
3495         }
3496         this.result = this.handleResponse(response);
3497         return this.result;
3498     },
3499
3500     // utility functions used internally
3501     getUrl : function(appendParams){
3502         var url = this.options.url || this.form.url || this.form.el.dom.action;
3503         if(appendParams){
3504             var p = this.getParams();
3505             if(p){
3506                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3507             }
3508         }
3509         return url;
3510     },
3511
3512     getMethod : function(){
3513         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3514     },
3515
3516     getParams : function(){
3517         var bp = this.form.baseParams;
3518         var p = this.options.params;
3519         if(p){
3520             if(typeof p == "object"){
3521                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3522             }else if(typeof p == 'string' && bp){
3523                 p += '&' + Roo.urlEncode(bp);
3524             }
3525         }else if(bp){
3526             p = Roo.urlEncode(bp);
3527         }
3528         return p;
3529     },
3530
3531     createCallback : function(){
3532         return {
3533             success: this.success,
3534             failure: this.failure,
3535             scope: this,
3536             timeout: (this.form.timeout*1000),
3537             upload: this.form.fileUpload ? this.success : undefined
3538         };
3539     }
3540 };
3541
3542 Roo.form.Action.Submit = function(form, options){
3543     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3544 };
3545
3546 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3547     type : 'submit',
3548
3549     haveProgress : false,
3550     uploadComplete : false,
3551     
3552     // uploadProgress indicator.
3553     uploadProgress : function()
3554     {
3555         if (!this.form.progressUrl) {
3556             return;
3557         }
3558         
3559         if (!this.haveProgress) {
3560             Roo.MessageBox.progress("Uploading", "Uploading");
3561         }
3562         if (this.uploadComplete) {
3563            Roo.MessageBox.hide();
3564            return;
3565         }
3566         
3567         this.haveProgress = true;
3568    
3569         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3570         
3571         var c = new Roo.data.Connection();
3572         c.request({
3573             url : this.form.progressUrl,
3574             params: {
3575                 id : uid
3576             },
3577             method: 'GET',
3578             success : function(req){
3579                //console.log(data);
3580                 var rdata = false;
3581                 var edata;
3582                 try  {
3583                    rdata = Roo.decode(req.responseText)
3584                 } catch (e) {
3585                     Roo.log("Invalid data from server..");
3586                     Roo.log(edata);
3587                     return;
3588                 }
3589                 if (!rdata || !rdata.success) {
3590                     Roo.log(rdata);
3591                     Roo.MessageBox.alert(Roo.encode(rdata));
3592                     return;
3593                 }
3594                 var data = rdata.data;
3595                 
3596                 if (this.uploadComplete) {
3597                    Roo.MessageBox.hide();
3598                    return;
3599                 }
3600                    
3601                 if (data){
3602                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3603                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3604                     );
3605                 }
3606                 this.uploadProgress.defer(2000,this);
3607             },
3608        
3609             failure: function(data) {
3610                 Roo.log('progress url failed ');
3611                 Roo.log(data);
3612             },
3613             scope : this
3614         });
3615            
3616     },
3617     
3618     
3619     run : function()
3620     {
3621         // run get Values on the form, so it syncs any secondary forms.
3622         this.form.getValues();
3623         
3624         var o = this.options;
3625         var method = this.getMethod();
3626         var isPost = method == 'POST';
3627         if(o.clientValidation === false || this.form.isValid()){
3628             
3629             if (this.form.progressUrl) {
3630                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3631                     (new Date() * 1) + '' + Math.random());
3632                     
3633             } 
3634             
3635             
3636             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3637                 form:this.form.el.dom,
3638                 url:this.getUrl(!isPost),
3639                 method: method,
3640                 params:isPost ? this.getParams() : null,
3641                 isUpload: this.form.fileUpload
3642             }));
3643             
3644             this.uploadProgress();
3645
3646         }else if (o.clientValidation !== false){ // client validation failed
3647             this.failureType = Roo.form.Action.CLIENT_INVALID;
3648             this.form.afterAction(this, false);
3649         }
3650     },
3651
3652     success : function(response)
3653     {
3654         this.uploadComplete= true;
3655         if (this.haveProgress) {
3656             Roo.MessageBox.hide();
3657         }
3658         
3659         
3660         var result = this.processResponse(response);
3661         if(result === true || result.success){
3662             this.form.afterAction(this, true);
3663             return;
3664         }
3665         if(result.errors){
3666             this.form.markInvalid(result.errors);
3667             this.failureType = Roo.form.Action.SERVER_INVALID;
3668         }
3669         this.form.afterAction(this, false);
3670     },
3671     failure : function(response)
3672     {
3673         this.uploadComplete= true;
3674         if (this.haveProgress) {
3675             Roo.MessageBox.hide();
3676         }
3677         
3678         this.response = response;
3679         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3680         this.form.afterAction(this, false);
3681     },
3682     
3683     handleResponse : function(response){
3684         if(this.form.errorReader){
3685             var rs = this.form.errorReader.read(response);
3686             var errors = [];
3687             if(rs.records){
3688                 for(var i = 0, len = rs.records.length; i < len; i++) {
3689                     var r = rs.records[i];
3690                     errors[i] = r.data;
3691                 }
3692             }
3693             if(errors.length < 1){
3694                 errors = null;
3695             }
3696             return {
3697                 success : rs.success,
3698                 errors : errors
3699             };
3700         }
3701         var ret = false;
3702         try {
3703             ret = Roo.decode(response.responseText);
3704         } catch (e) {
3705             ret = {
3706                 success: false,
3707                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3708                 errors : []
3709             };
3710         }
3711         return ret;
3712         
3713     }
3714 });
3715
3716
3717 Roo.form.Action.Load = function(form, options){
3718     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3719     this.reader = this.form.reader;
3720 };
3721
3722 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3723     type : 'load',
3724
3725     run : function(){
3726         
3727         Roo.Ajax.request(Roo.apply(
3728                 this.createCallback(), {
3729                     method:this.getMethod(),
3730                     url:this.getUrl(false),
3731                     params:this.getParams()
3732         }));
3733     },
3734
3735     success : function(response){
3736         
3737         var result = this.processResponse(response);
3738         if(result === true || !result.success || !result.data){
3739             this.failureType = Roo.form.Action.LOAD_FAILURE;
3740             this.form.afterAction(this, false);
3741             return;
3742         }
3743         this.form.clearInvalid();
3744         this.form.setValues(result.data);
3745         this.form.afterAction(this, true);
3746     },
3747
3748     handleResponse : function(response){
3749         if(this.form.reader){
3750             var rs = this.form.reader.read(response);
3751             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3752             return {
3753                 success : rs.success,
3754                 data : data
3755             };
3756         }
3757         return Roo.decode(response.responseText);
3758     }
3759 });
3760
3761 Roo.form.Action.ACTION_TYPES = {
3762     'load' : Roo.form.Action.Load,
3763     'submit' : Roo.form.Action.Submit
3764 };/*
3765  * - LGPL
3766  *
3767  * form
3768  * 
3769  */
3770
3771 /**
3772  * @class Roo.bootstrap.Form
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap Form class
3775  * @cfg {String} method  GET | POST (default POST)
3776  * @cfg {String} labelAlign top | left (default top)
3777   * @cfg {String} align left  | right - for navbars
3778
3779  * 
3780  * @constructor
3781  * Create a new Form
3782  * @param {Object} config The config object
3783  */
3784
3785
3786 Roo.bootstrap.Form = function(config){
3787     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3788     this.addEvents({
3789         /**
3790          * @event clientvalidation
3791          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3792          * @param {Form} this
3793          * @param {Boolean} valid true if the form has passed client-side validation
3794          */
3795         clientvalidation: true,
3796         /**
3797          * @event beforeaction
3798          * Fires before any action is performed. Return false to cancel the action.
3799          * @param {Form} this
3800          * @param {Action} action The action to be performed
3801          */
3802         beforeaction: true,
3803         /**
3804          * @event actionfailed
3805          * Fires when an action fails.
3806          * @param {Form} this
3807          * @param {Action} action The action that failed
3808          */
3809         actionfailed : true,
3810         /**
3811          * @event actioncomplete
3812          * Fires when an action is completed.
3813          * @param {Form} this
3814          * @param {Action} action The action that completed
3815          */
3816         actioncomplete : true
3817     });
3818     
3819 };
3820
3821 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3822       
3823      /**
3824      * @cfg {String} method
3825      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3826      */
3827     method : 'POST',
3828     /**
3829      * @cfg {String} url
3830      * The URL to use for form actions if one isn't supplied in the action options.
3831      */
3832     /**
3833      * @cfg {Boolean} fileUpload
3834      * Set to true if this form is a file upload.
3835      */
3836      
3837     /**
3838      * @cfg {Object} baseParams
3839      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3840      */
3841       
3842     /**
3843      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3844      */
3845     timeout: 30,
3846     /**
3847      * @cfg {Sting} align (left|right) for navbar forms
3848      */
3849     align : 'left',
3850
3851     // private
3852     activeAction : null,
3853  
3854     /**
3855      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3856      * element by passing it or its id or mask the form itself by passing in true.
3857      * @type Mixed
3858      */
3859     waitMsgTarget : false,
3860     
3861      
3862     
3863     /**
3864      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3865      * element by passing it or its id or mask the form itself by passing in true.
3866      * @type Mixed
3867      */
3868     
3869     getAutoCreate : function(){
3870         
3871         var cfg = {
3872             tag: 'form',
3873             method : this.method || 'POST',
3874             id : this.id || Roo.id(),
3875             cls : ''
3876         }
3877         if (this.parent().xtype.match(/^Nav/)) {
3878             cfg.cls = 'navbar-form navbar-' + this.align;
3879             
3880         }
3881         
3882         if (this.labelAlign == 'left' ) {
3883             cfg.cls += ' form-horizontal';
3884         }
3885         
3886         
3887         return cfg;
3888     },
3889     initEvents : function()
3890     {
3891         this.el.on('submit', this.onSubmit, this);
3892         
3893         
3894     },
3895     // private
3896     onSubmit : function(e){
3897         e.stopEvent();
3898     },
3899     
3900      /**
3901      * Returns true if client-side validation on the form is successful.
3902      * @return Boolean
3903      */
3904     isValid : function(){
3905         var items = this.getItems();
3906         var valid = true;
3907         items.each(function(f){
3908            if(!f.validate()){
3909                valid = false;
3910                
3911            }
3912         });
3913         return valid;
3914     },
3915     /**
3916      * Returns true if any fields in this form have changed since their original load.
3917      * @return Boolean
3918      */
3919     isDirty : function(){
3920         var dirty = false;
3921         var items = this.getItems();
3922         items.each(function(f){
3923            if(f.isDirty()){
3924                dirty = true;
3925                return false;
3926            }
3927            return true;
3928         });
3929         return dirty;
3930     },
3931      /**
3932      * Performs a predefined action (submit or load) or custom actions you define on this form.
3933      * @param {String} actionName The name of the action type
3934      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3935      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3936      * accept other config options):
3937      * <pre>
3938 Property          Type             Description
3939 ----------------  ---------------  ----------------------------------------------------------------------------------
3940 url               String           The url for the action (defaults to the form's url)
3941 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3942 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3943 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3944                                    validate the form on the client (defaults to false)
3945      * </pre>
3946      * @return {BasicForm} this
3947      */
3948     doAction : function(action, options){
3949         if(typeof action == 'string'){
3950             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3951         }
3952         if(this.fireEvent('beforeaction', this, action) !== false){
3953             this.beforeAction(action);
3954             action.run.defer(100, action);
3955         }
3956         return this;
3957     },
3958     
3959     // private
3960     beforeAction : function(action){
3961         var o = action.options;
3962         
3963         // not really supported yet.. ??
3964         
3965         //if(this.waitMsgTarget === true){
3966             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3967         //}else if(this.waitMsgTarget){
3968         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3969         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3970         //}else {
3971         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3972        // }
3973          
3974     },
3975
3976     // private
3977     afterAction : function(action, success){
3978         this.activeAction = null;
3979         var o = action.options;
3980         
3981         //if(this.waitMsgTarget === true){
3982             this.el.unmask();
3983         //}else if(this.waitMsgTarget){
3984         //    this.waitMsgTarget.unmask();
3985         //}else{
3986         //    Roo.MessageBox.updateProgress(1);
3987         //    Roo.MessageBox.hide();
3988        // }
3989         // 
3990         if(success){
3991             if(o.reset){
3992                 this.reset();
3993             }
3994             Roo.callback(o.success, o.scope, [this, action]);
3995             this.fireEvent('actioncomplete', this, action);
3996             
3997         }else{
3998             
3999             // failure condition..
4000             // we have a scenario where updates need confirming.
4001             // eg. if a locking scenario exists..
4002             // we look for { errors : { needs_confirm : true }} in the response.
4003             if (
4004                 (typeof(action.result) != 'undefined')  &&
4005                 (typeof(action.result.errors) != 'undefined')  &&
4006                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4007            ){
4008                 var _t = this;
4009                 Roo.log("not supported yet");
4010                  /*
4011                 
4012                 Roo.MessageBox.confirm(
4013                     "Change requires confirmation",
4014                     action.result.errorMsg,
4015                     function(r) {
4016                         if (r != 'yes') {
4017                             return;
4018                         }
4019                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4020                     }
4021                     
4022                 );
4023                 */
4024                 
4025                 
4026                 return;
4027             }
4028             
4029             Roo.callback(o.failure, o.scope, [this, action]);
4030             // show an error message if no failed handler is set..
4031             if (!this.hasListener('actionfailed')) {
4032                 Roo.log("need to add dialog support");
4033                 /*
4034                 Roo.MessageBox.alert("Error",
4035                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4036                         action.result.errorMsg :
4037                         "Saving Failed, please check your entries or try again"
4038                 );
4039                 */
4040             }
4041             
4042             this.fireEvent('actionfailed', this, action);
4043         }
4044         
4045     },
4046     /**
4047      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4048      * @param {String} id The value to search for
4049      * @return Field
4050      */
4051     findField : function(id){
4052         var items = this.getItems();
4053         var field = items.get(id);
4054         if(!field){
4055              items.each(function(f){
4056                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4057                     field = f;
4058                     return false;
4059                 }
4060                 return true;
4061             });
4062         }
4063         return field || null;
4064     },
4065      /**
4066      * Mark fields in this form invalid in bulk.
4067      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4068      * @return {BasicForm} this
4069      */
4070     markInvalid : function(errors){
4071         if(errors instanceof Array){
4072             for(var i = 0, len = errors.length; i < len; i++){
4073                 var fieldError = errors[i];
4074                 var f = this.findField(fieldError.id);
4075                 if(f){
4076                     f.markInvalid(fieldError.msg);
4077                 }
4078             }
4079         }else{
4080             var field, id;
4081             for(id in errors){
4082                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4083                     field.markInvalid(errors[id]);
4084                 }
4085             }
4086         }
4087         //Roo.each(this.childForms || [], function (f) {
4088         //    f.markInvalid(errors);
4089         //});
4090         
4091         return this;
4092     },
4093
4094     /**
4095      * Set values for fields in this form in bulk.
4096      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4097      * @return {BasicForm} this
4098      */
4099     setValues : function(values){
4100         if(values instanceof Array){ // array of objects
4101             for(var i = 0, len = values.length; i < len; i++){
4102                 var v = values[i];
4103                 var f = this.findField(v.id);
4104                 if(f){
4105                     f.setValue(v.value);
4106                     if(this.trackResetOnLoad){
4107                         f.originalValue = f.getValue();
4108                     }
4109                 }
4110             }
4111         }else{ // object hash
4112             var field, id;
4113             for(id in values){
4114                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4115                     
4116                     if (field.setFromData && 
4117                         field.valueField && 
4118                         field.displayField &&
4119                         // combos' with local stores can 
4120                         // be queried via setValue()
4121                         // to set their value..
4122                         (field.store && !field.store.isLocal)
4123                         ) {
4124                         // it's a combo
4125                         var sd = { };
4126                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4127                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4128                         field.setFromData(sd);
4129                         
4130                     } else {
4131                         field.setValue(values[id]);
4132                     }
4133                     
4134                     
4135                     if(this.trackResetOnLoad){
4136                         field.originalValue = field.getValue();
4137                     }
4138                 }
4139             }
4140         }
4141          
4142         //Roo.each(this.childForms || [], function (f) {
4143         //    f.setValues(values);
4144         //});
4145                 
4146         return this;
4147     },
4148
4149     /**
4150      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4151      * they are returned as an array.
4152      * @param {Boolean} asString
4153      * @return {Object}
4154      */
4155     getValues : function(asString){
4156         //if (this.childForms) {
4157             // copy values from the child forms
4158         //    Roo.each(this.childForms, function (f) {
4159         //        this.setValues(f.getValues());
4160         //    }, this);
4161         //}
4162         
4163         
4164         
4165         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4166         if(asString === true){
4167             return fs;
4168         }
4169         return Roo.urlDecode(fs);
4170     },
4171     
4172     /**
4173      * Returns the fields in this form as an object with key/value pairs. 
4174      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4175      * @return {Object}
4176      */
4177     getFieldValues : function(with_hidden)
4178     {
4179         var items = this.getItems();
4180         var ret = {};
4181         items.each(function(f){
4182             if (!f.getName()) {
4183                 return;
4184             }
4185             var v = f.getValue();
4186             if (f.inputType =='radio') {
4187                 if (typeof(ret[f.getName()]) == 'undefined') {
4188                     ret[f.getName()] = ''; // empty..
4189                 }
4190                 
4191                 if (!f.el.dom.checked) {
4192                     return;
4193                     
4194                 }
4195                 v = f.el.dom.value;
4196                 
4197             }
4198             
4199             // not sure if this supported any more..
4200             if ((typeof(v) == 'object') && f.getRawValue) {
4201                 v = f.getRawValue() ; // dates..
4202             }
4203             // combo boxes where name != hiddenName...
4204             if (f.name != f.getName()) {
4205                 ret[f.name] = f.getRawValue();
4206             }
4207             ret[f.getName()] = v;
4208         });
4209         
4210         return ret;
4211     },
4212
4213     /**
4214      * Clears all invalid messages in this form.
4215      * @return {BasicForm} this
4216      */
4217     clearInvalid : function(){
4218         var items = this.getItems();
4219         
4220         items.each(function(f){
4221            f.clearInvalid();
4222         });
4223         
4224         
4225         
4226         return this;
4227     },
4228
4229     /**
4230      * Resets this form.
4231      * @return {BasicForm} this
4232      */
4233     reset : function(){
4234         var items = this.getItems();
4235         items.each(function(f){
4236             f.reset();
4237         });
4238         
4239         Roo.each(this.childForms || [], function (f) {
4240             f.reset();
4241         });
4242        
4243         
4244         return this;
4245     },
4246     getItems : function()
4247     {
4248         var r=new Roo.util.MixedCollection(false, function(o){
4249             return o.id || (o.id = Roo.id());
4250         });
4251         var iter = function(el) {
4252             if (el.inputEl) {
4253                 r.add(el);
4254             }
4255             if (!el.items) {
4256                 return;
4257             }
4258             Roo.each(el.items,function(e) {
4259                 iter(e);
4260             });
4261             
4262             
4263         };
4264         iter(this);
4265         return r;
4266         
4267         
4268         
4269         
4270     }
4271     
4272 });
4273
4274  
4275 /*
4276  * Based on:
4277  * Ext JS Library 1.1.1
4278  * Copyright(c) 2006-2007, Ext JS, LLC.
4279  *
4280  * Originally Released Under LGPL - original licence link has changed is not relivant.
4281  *
4282  * Fork - LGPL
4283  * <script type="text/javascript">
4284  */
4285 /**
4286  * @class Roo.form.VTypes
4287  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4288  * @singleton
4289  */
4290 Roo.form.VTypes = function(){
4291     // closure these in so they are only created once.
4292     var alpha = /^[a-zA-Z_]+$/;
4293     var alphanum = /^[a-zA-Z0-9_]+$/;
4294     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4295     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4296
4297     // All these messages and functions are configurable
4298     return {
4299         /**
4300          * The function used to validate email addresses
4301          * @param {String} value The email address
4302          */
4303         'email' : function(v){
4304             return email.test(v);
4305         },
4306         /**
4307          * The error text to display when the email validation function returns false
4308          * @type String
4309          */
4310         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4311         /**
4312          * The keystroke filter mask to be applied on email input
4313          * @type RegExp
4314          */
4315         'emailMask' : /[a-z0-9_\.\-@]/i,
4316
4317         /**
4318          * The function used to validate URLs
4319          * @param {String} value The URL
4320          */
4321         'url' : function(v){
4322             return url.test(v);
4323         },
4324         /**
4325          * The error text to display when the url validation function returns false
4326          * @type String
4327          */
4328         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4329         
4330         /**
4331          * The function used to validate alpha values
4332          * @param {String} value The value
4333          */
4334         'alpha' : function(v){
4335             return alpha.test(v);
4336         },
4337         /**
4338          * The error text to display when the alpha validation function returns false
4339          * @type String
4340          */
4341         'alphaText' : 'This field should only contain letters and _',
4342         /**
4343          * The keystroke filter mask to be applied on alpha input
4344          * @type RegExp
4345          */
4346         'alphaMask' : /[a-z_]/i,
4347
4348         /**
4349          * The function used to validate alphanumeric values
4350          * @param {String} value The value
4351          */
4352         'alphanum' : function(v){
4353             return alphanum.test(v);
4354         },
4355         /**
4356          * The error text to display when the alphanumeric validation function returns false
4357          * @type String
4358          */
4359         'alphanumText' : 'This field should only contain letters, numbers and _',
4360         /**
4361          * The keystroke filter mask to be applied on alphanumeric input
4362          * @type RegExp
4363          */
4364         'alphanumMask' : /[a-z0-9_]/i
4365     };
4366 }();/*
4367  * - LGPL
4368  *
4369  * Input
4370  * 
4371  */
4372
4373 /**
4374  * @class Roo.bootstrap.Input
4375  * @extends Roo.bootstrap.Component
4376  * Bootstrap Input class
4377  * @cfg {Boolean} disabled is it disabled
4378  * @cfg {String} fieldLabel - the label associated
4379  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4380  * @cfg {String} name name of the input
4381  * @cfg {string} fieldLabel - the label associated
4382  * @cfg {string}  inputType - input / file submit ...
4383  * @cfg {string} placeholder - placeholder to put in text.
4384  * @cfg {string}  before - input group add on before
4385  * @cfg {string} after - input group add on after
4386  * @cfg {string} size - (lg|sm) or leave empty..
4387  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4388  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4389  * @cfg {Number} md colspan out of 12 for computer-sized screens
4390  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4391  * @cfg {string} value default value of the input
4392  * @cfg {Number} labelWidth set the width of label (0-12)
4393  * @cfg {String} labelAlign (top|left)
4394  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4395  * 
4396  * 
4397  * @constructor
4398  * Create a new Input
4399  * @param {Object} config The config object
4400  */
4401
4402 Roo.bootstrap.Input = function(config){
4403     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4404    
4405         this.addEvents({
4406             /**
4407              * @event focus
4408              * Fires when this field receives input focus.
4409              * @param {Roo.form.Field} this
4410              */
4411             focus : true,
4412             /**
4413              * @event blur
4414              * Fires when this field loses input focus.
4415              * @param {Roo.form.Field} this
4416              */
4417             blur : true,
4418             /**
4419              * @event specialkey
4420              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4421              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4422              * @param {Roo.form.Field} this
4423              * @param {Roo.EventObject} e The event object
4424              */
4425             specialkey : true,
4426             /**
4427              * @event change
4428              * Fires just before the field blurs if the field value has changed.
4429              * @param {Roo.form.Field} this
4430              * @param {Mixed} newValue The new value
4431              * @param {Mixed} oldValue The original value
4432              */
4433             change : true,
4434             /**
4435              * @event invalid
4436              * Fires after the field has been marked as invalid.
4437              * @param {Roo.form.Field} this
4438              * @param {String} msg The validation message
4439              */
4440             invalid : true,
4441             /**
4442              * @event valid
4443              * Fires after the field has been validated with no errors.
4444              * @param {Roo.form.Field} this
4445              */
4446             valid : true,
4447              /**
4448              * @event keyup
4449              * Fires after the key up
4450              * @param {Roo.form.Field} this
4451              * @param {Roo.EventObject}  e The event Object
4452              */
4453             keyup : true
4454         });
4455 };
4456
4457 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4458      /**
4459      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4460       automatic validation (defaults to "keyup").
4461      */
4462     validationEvent : "keyup",
4463      /**
4464      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4465      */
4466     validateOnBlur : true,
4467     /**
4468      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4469      */
4470     validationDelay : 250,
4471      /**
4472      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4473      */
4474     focusClass : "x-form-focus",  // not needed???
4475     
4476        
4477     /**
4478      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4479      */
4480     invalidClass : "has-error",
4481     
4482     /**
4483      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4484      */
4485     selectOnFocus : false,
4486     
4487      /**
4488      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4489      */
4490     maskRe : null,
4491        /**
4492      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4493      */
4494     vtype : null,
4495     
4496       /**
4497      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4498      */
4499     disableKeyFilter : false,
4500     
4501        /**
4502      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4503      */
4504     disabled : false,
4505      /**
4506      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4507      */
4508     allowBlank : true,
4509     /**
4510      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4511      */
4512     blankText : "This field is required",
4513     
4514      /**
4515      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4516      */
4517     minLength : 0,
4518     /**
4519      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4520      */
4521     maxLength : Number.MAX_VALUE,
4522     /**
4523      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4524      */
4525     minLengthText : "The minimum length for this field is {0}",
4526     /**
4527      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4528      */
4529     maxLengthText : "The maximum length for this field is {0}",
4530   
4531     
4532     /**
4533      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4534      * If available, this function will be called only after the basic validators all return true, and will be passed the
4535      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4536      */
4537     validator : null,
4538     /**
4539      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4540      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4541      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4542      */
4543     regex : null,
4544     /**
4545      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4546      */
4547     regexText : "",
4548     
4549     
4550     
4551     fieldLabel : '',
4552     inputType : 'text',
4553     
4554     name : false,
4555     placeholder: false,
4556     before : false,
4557     after : false,
4558     size : false,
4559     // private
4560     hasFocus : false,
4561     preventMark: false,
4562     isFormField : true,
4563     value : '',
4564     labelWidth : 2,
4565     labelAlign : false,
4566     readOnly : false,
4567     
4568     parentLabelAlign : function()
4569     {
4570         var parent = this;
4571         while (parent.parent()) {
4572             parent = parent.parent();
4573             if (typeof(parent.labelAlign) !='undefined') {
4574                 return parent.labelAlign;
4575             }
4576         }
4577         return 'left';
4578         
4579     },
4580     
4581     getAutoCreate : function(){
4582         
4583         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4584         
4585         var id = Roo.id();
4586         
4587         var cfg = {};
4588         
4589         if(this.inputType != 'hidden'){
4590             cfg.cls = 'form-group' //input-group
4591         }
4592         
4593         var input =  {
4594             tag: 'input',
4595             id : id,
4596             type : this.inputType,
4597             value : this.value,
4598             cls : 'form-control',
4599             placeholder : this.placeholder || ''
4600             
4601         };
4602         
4603         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4604             input.maxLength = this.maxLength;
4605         }
4606         
4607         if (this.disabled) {
4608             input.disabled=true;
4609         }
4610         
4611         if (this.readOnly) {
4612             input.readonly=true;
4613         }
4614         
4615         if (this.name) {
4616             input.name = this.name;
4617         }
4618         if (this.size) {
4619             input.cls += ' input-' + this.size;
4620         }
4621         var settings=this;
4622         ['xs','sm','md','lg'].map(function(size){
4623             if (settings[size]) {
4624                 cfg.cls += ' col-' + size + '-' + settings[size];
4625             }
4626         });
4627         
4628         var inputblock = input;
4629         
4630         if (this.before || this.after) {
4631             
4632             inputblock = {
4633                 cls : 'input-group',
4634                 cn :  [] 
4635             };
4636             if (this.before) {
4637                 inputblock.cn.push({
4638                     tag :'span',
4639                     cls : 'input-group-addon',
4640                     html : this.before
4641                 });
4642             }
4643             inputblock.cn.push(input);
4644             if (this.after) {
4645                 inputblock.cn.push({
4646                     tag :'span',
4647                     cls : 'input-group-addon',
4648                     html : this.after
4649                 });
4650             }
4651             
4652         };
4653         
4654         if (align ==='left' && this.fieldLabel.length) {
4655                 Roo.log("left and has label");
4656                 cfg.cn = [
4657                     
4658                     {
4659                         tag: 'label',
4660                         'for' :  id,
4661                         cls : 'control-label col-sm-' + this.labelWidth,
4662                         html : this.fieldLabel
4663                         
4664                     },
4665                     {
4666                         cls : "col-sm-" + (12 - this.labelWidth), 
4667                         cn: [
4668                             inputblock
4669                         ]
4670                     }
4671                     
4672                 ];
4673         } else if ( this.fieldLabel.length) {
4674                 Roo.log(" label");
4675                  cfg.cn = [
4676                    
4677                     {
4678                         tag: 'label',
4679                         //cls : 'input-group-addon',
4680                         html : this.fieldLabel
4681                         
4682                     },
4683                     
4684                     inputblock
4685                     
4686                 ];
4687
4688         } else {
4689             
4690                 Roo.log(" no label && no align");
4691                 cfg.cn = [
4692                     
4693                         inputblock
4694                     
4695                 ];
4696                 
4697                 
4698         };
4699         Roo.log('input-parentType: ' + this.parentType);
4700         
4701         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4702            cfg.cls += ' navbar-form';
4703            Roo.log(cfg);
4704         }
4705         
4706         return cfg;
4707         
4708     },
4709     /**
4710      * return the real input element.
4711      */
4712     inputEl: function ()
4713     {
4714         return this.el.select('input.form-control',true).first();
4715     },
4716     setDisabled : function(v)
4717     {
4718         var i  = this.inputEl().dom;
4719         if (!v) {
4720             i.removeAttribute('disabled');
4721             return;
4722             
4723         }
4724         i.setAttribute('disabled','true');
4725     },
4726     initEvents : function()
4727     {
4728         
4729         this.inputEl().on("keydown" , this.fireKey,  this);
4730         this.inputEl().on("focus", this.onFocus,  this);
4731         this.inputEl().on("blur", this.onBlur,  this);
4732         
4733         this.inputEl().relayEvent('keyup', this);
4734
4735         // reference to original value for reset
4736         this.originalValue = this.getValue();
4737         //Roo.form.TextField.superclass.initEvents.call(this);
4738         if(this.validationEvent == 'keyup'){
4739             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4740             this.inputEl().on('keyup', this.filterValidation, this);
4741         }
4742         else if(this.validationEvent !== false){
4743             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4744         }
4745         
4746         if(this.selectOnFocus){
4747             this.on("focus", this.preFocus, this);
4748             
4749         }
4750         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4751             this.inputEl().on("keypress", this.filterKeys, this);
4752         }
4753        /* if(this.grow){
4754             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4755             this.el.on("click", this.autoSize,  this);
4756         }
4757         */
4758         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4759             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4760         }
4761         
4762     },
4763     filterValidation : function(e){
4764         if(!e.isNavKeyPress()){
4765             this.validationTask.delay(this.validationDelay);
4766         }
4767     },
4768      /**
4769      * Validates the field value
4770      * @return {Boolean} True if the value is valid, else false
4771      */
4772     validate : function(){
4773         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4774         if(this.disabled || this.validateValue(this.getRawValue())){
4775             this.clearInvalid();
4776             return true;
4777         }
4778         return false;
4779     },
4780     
4781     
4782     /**
4783      * Validates a value according to the field's validation rules and marks the field as invalid
4784      * if the validation fails
4785      * @param {Mixed} value The value to validate
4786      * @return {Boolean} True if the value is valid, else false
4787      */
4788     validateValue : function(value){
4789         if(value.length < 1)  { // if it's blank
4790              if(this.allowBlank){
4791                 this.clearInvalid();
4792                 return true;
4793              }else{
4794                 this.markInvalid(this.blankText);
4795                 return false;
4796              }
4797         }
4798         if(value.length < this.minLength){
4799             this.markInvalid(String.format(this.minLengthText, this.minLength));
4800             return false;
4801         }
4802         if(value.length > this.maxLength){
4803             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4804             return false;
4805         }
4806         if(this.vtype){
4807             var vt = Roo.form.VTypes;
4808             if(!vt[this.vtype](value, this)){
4809                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4810                 return false;
4811             }
4812         }
4813         if(typeof this.validator == "function"){
4814             var msg = this.validator(value);
4815             if(msg !== true){
4816                 this.markInvalid(msg);
4817                 return false;
4818             }
4819         }
4820         if(this.regex && !this.regex.test(value)){
4821             this.markInvalid(this.regexText);
4822             return false;
4823         }
4824         return true;
4825     },
4826
4827     
4828     
4829      // private
4830     fireKey : function(e){
4831         //Roo.log('field ' + e.getKey());
4832         if(e.isNavKeyPress()){
4833             this.fireEvent("specialkey", this, e);
4834         }
4835     },
4836     focus : function (selectText){
4837         if(this.rendered){
4838             this.inputEl().focus();
4839             if(selectText === true){
4840                 this.inputEl().dom.select();
4841             }
4842         }
4843         return this;
4844     } ,
4845     
4846     onFocus : function(){
4847         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4848            // this.el.addClass(this.focusClass);
4849         }
4850         if(!this.hasFocus){
4851             this.hasFocus = true;
4852             this.startValue = this.getValue();
4853             this.fireEvent("focus", this);
4854         }
4855     },
4856     
4857     beforeBlur : Roo.emptyFn,
4858
4859     
4860     // private
4861     onBlur : function(){
4862         this.beforeBlur();
4863         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4864             //this.el.removeClass(this.focusClass);
4865         }
4866         this.hasFocus = false;
4867         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4868             this.validate();
4869         }
4870         var v = this.getValue();
4871         if(String(v) !== String(this.startValue)){
4872             this.fireEvent('change', this, v, this.startValue);
4873         }
4874         this.fireEvent("blur", this);
4875     },
4876     
4877     /**
4878      * Resets the current field value to the originally loaded value and clears any validation messages
4879      */
4880     reset : function(){
4881         this.setValue(this.originalValue);
4882         this.clearInvalid();
4883     },
4884      /**
4885      * Returns the name of the field
4886      * @return {Mixed} name The name field
4887      */
4888     getName: function(){
4889         return this.name;
4890     },
4891      /**
4892      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4893      * @return {Mixed} value The field value
4894      */
4895     getValue : function(){
4896         return this.inputEl().getValue();
4897     },
4898     /**
4899      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4900      * @return {Mixed} value The field value
4901      */
4902     getRawValue : function(){
4903         var v = this.inputEl().getValue();
4904         
4905         return v;
4906     },
4907     
4908     /**
4909      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4910      * @param {Mixed} value The value to set
4911      */
4912     setRawValue : function(v){
4913         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4914     },
4915     
4916     selectText : function(start, end){
4917         var v = this.getRawValue();
4918         if(v.length > 0){
4919             start = start === undefined ? 0 : start;
4920             end = end === undefined ? v.length : end;
4921             var d = this.inputEl().dom;
4922             if(d.setSelectionRange){
4923                 d.setSelectionRange(start, end);
4924             }else if(d.createTextRange){
4925                 var range = d.createTextRange();
4926                 range.moveStart("character", start);
4927                 range.moveEnd("character", v.length-end);
4928                 range.select();
4929             }
4930         }
4931     },
4932     
4933     /**
4934      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4935      * @param {Mixed} value The value to set
4936      */
4937     setValue : function(v){
4938         this.value = v;
4939         if(this.rendered){
4940             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4941             this.validate();
4942         }
4943     },
4944     
4945     /*
4946     processValue : function(value){
4947         if(this.stripCharsRe){
4948             var newValue = value.replace(this.stripCharsRe, '');
4949             if(newValue !== value){
4950                 this.setRawValue(newValue);
4951                 return newValue;
4952             }
4953         }
4954         return value;
4955     },
4956   */
4957     preFocus : function(){
4958         
4959         if(this.selectOnFocus){
4960             this.inputEl().dom.select();
4961         }
4962     },
4963     filterKeys : function(e){
4964         var k = e.getKey();
4965         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4966             return;
4967         }
4968         var c = e.getCharCode(), cc = String.fromCharCode(c);
4969         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4970             return;
4971         }
4972         if(!this.maskRe.test(cc)){
4973             e.stopEvent();
4974         }
4975     },
4976      /**
4977      * Clear any invalid styles/messages for this field
4978      */
4979     clearInvalid : function(){
4980         
4981         if(!this.el || this.preventMark){ // not rendered
4982             return;
4983         }
4984         this.el.removeClass(this.invalidClass);
4985         /*
4986         switch(this.msgTarget){
4987             case 'qtip':
4988                 this.el.dom.qtip = '';
4989                 break;
4990             case 'title':
4991                 this.el.dom.title = '';
4992                 break;
4993             case 'under':
4994                 if(this.errorEl){
4995                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4996                 }
4997                 break;
4998             case 'side':
4999                 if(this.errorIcon){
5000                     this.errorIcon.dom.qtip = '';
5001                     this.errorIcon.hide();
5002                     this.un('resize', this.alignErrorIcon, this);
5003                 }
5004                 break;
5005             default:
5006                 var t = Roo.getDom(this.msgTarget);
5007                 t.innerHTML = '';
5008                 t.style.display = 'none';
5009                 break;
5010         }
5011         */
5012         this.fireEvent('valid', this);
5013     },
5014      /**
5015      * Mark this field as invalid
5016      * @param {String} msg The validation message
5017      */
5018     markInvalid : function(msg){
5019         if(!this.el  || this.preventMark){ // not rendered
5020             return;
5021         }
5022         this.el.addClass(this.invalidClass);
5023         /*
5024         msg = msg || this.invalidText;
5025         switch(this.msgTarget){
5026             case 'qtip':
5027                 this.el.dom.qtip = msg;
5028                 this.el.dom.qclass = 'x-form-invalid-tip';
5029                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5030                     Roo.QuickTips.enable();
5031                 }
5032                 break;
5033             case 'title':
5034                 this.el.dom.title = msg;
5035                 break;
5036             case 'under':
5037                 if(!this.errorEl){
5038                     var elp = this.el.findParent('.x-form-element', 5, true);
5039                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5040                     this.errorEl.setWidth(elp.getWidth(true)-20);
5041                 }
5042                 this.errorEl.update(msg);
5043                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5044                 break;
5045             case 'side':
5046                 if(!this.errorIcon){
5047                     var elp = this.el.findParent('.x-form-element', 5, true);
5048                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5049                 }
5050                 this.alignErrorIcon();
5051                 this.errorIcon.dom.qtip = msg;
5052                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5053                 this.errorIcon.show();
5054                 this.on('resize', this.alignErrorIcon, this);
5055                 break;
5056             default:
5057                 var t = Roo.getDom(this.msgTarget);
5058                 t.innerHTML = msg;
5059                 t.style.display = this.msgDisplay;
5060                 break;
5061         }
5062         */
5063         this.fireEvent('invalid', this, msg);
5064     },
5065     // private
5066     SafariOnKeyDown : function(event)
5067     {
5068         // this is a workaround for a password hang bug on chrome/ webkit.
5069         
5070         var isSelectAll = false;
5071         
5072         if(this.inputEl().dom.selectionEnd > 0){
5073             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5074         }
5075         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5076             event.preventDefault();
5077             this.setValue('');
5078             return;
5079         }
5080         
5081         if(isSelectAll){ // backspace and delete key
5082             
5083             event.preventDefault();
5084             // this is very hacky as keydown always get's upper case.
5085             //
5086             var cc = String.fromCharCode(event.getCharCode());
5087             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5088             
5089         }
5090     },
5091     adjustWidth : function(tag, w){
5092         tag = tag.toLowerCase();
5093         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5094             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5095                 if(tag == 'input'){
5096                     return w + 2;
5097                 }
5098                 if(tag == 'textarea'){
5099                     return w-2;
5100                 }
5101             }else if(Roo.isOpera){
5102                 if(tag == 'input'){
5103                     return w + 2;
5104                 }
5105                 if(tag == 'textarea'){
5106                     return w-2;
5107                 }
5108             }
5109         }
5110         return w;
5111     }
5112     
5113 });
5114
5115  
5116 /*
5117  * - LGPL
5118  *
5119  * Input
5120  * 
5121  */
5122
5123 /**
5124  * @class Roo.bootstrap.TextArea
5125  * @extends Roo.bootstrap.Input
5126  * Bootstrap TextArea class
5127  * @cfg {Number} cols Specifies the visible width of a text area
5128  * @cfg {Number} rows Specifies the visible number of lines in a text area
5129  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5130  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5131  * @cfg {string} html text
5132  * 
5133  * @constructor
5134  * Create a new TextArea
5135  * @param {Object} config The config object
5136  */
5137
5138 Roo.bootstrap.TextArea = function(config){
5139     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5140    
5141 };
5142
5143 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5144      
5145     cols : false,
5146     rows : 5,
5147     readOnly : false,
5148     warp : 'soft',
5149     resize : false,
5150     value: false,
5151     html: false,
5152     
5153     getAutoCreate : function(){
5154         
5155         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5156         
5157         var id = Roo.id();
5158         
5159         var cfg = {};
5160         
5161         var input =  {
5162             tag: 'textarea',
5163             id : id,
5164             warp : this.warp,
5165             rows : this.rows,
5166             value : this.value || '',
5167             html: this.html || '',
5168             cls : 'form-control',
5169             placeholder : this.placeholder || '' 
5170             
5171         };
5172         
5173         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5174             input.maxLength = this.maxLength;
5175         }
5176         
5177         if(this.resize){
5178             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5179         }
5180         
5181         if(this.cols){
5182             input.cols = this.cols;
5183         }
5184         
5185         if (this.readOnly) {
5186             input.readonly = true;
5187         }
5188         
5189         if (this.name) {
5190             input.name = this.name;
5191         }
5192         
5193         if (this.size) {
5194             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5195         }
5196         
5197         var settings=this;
5198         ['xs','sm','md','lg'].map(function(size){
5199             if (settings[size]) {
5200                 cfg.cls += ' col-' + size + '-' + settings[size];
5201             }
5202         });
5203         
5204         var inputblock = input;
5205         
5206         if (this.before || this.after) {
5207             
5208             inputblock = {
5209                 cls : 'input-group',
5210                 cn :  [] 
5211             };
5212             if (this.before) {
5213                 inputblock.cn.push({
5214                     tag :'span',
5215                     cls : 'input-group-addon',
5216                     html : this.before
5217                 });
5218             }
5219             inputblock.cn.push(input);
5220             if (this.after) {
5221                 inputblock.cn.push({
5222                     tag :'span',
5223                     cls : 'input-group-addon',
5224                     html : this.after
5225                 });
5226             }
5227             
5228         }
5229         
5230         if (align ==='left' && this.fieldLabel.length) {
5231                 Roo.log("left and has label");
5232                 cfg.cn = [
5233                     
5234                     {
5235                         tag: 'label',
5236                         'for' :  id,
5237                         cls : 'control-label col-sm-' + this.labelWidth,
5238                         html : this.fieldLabel
5239                         
5240                     },
5241                     {
5242                         cls : "col-sm-" + (12 - this.labelWidth), 
5243                         cn: [
5244                             inputblock
5245                         ]
5246                     }
5247                     
5248                 ];
5249         } else if ( this.fieldLabel.length) {
5250                 Roo.log(" label");
5251                  cfg.cn = [
5252                    
5253                     {
5254                         tag: 'label',
5255                         //cls : 'input-group-addon',
5256                         html : this.fieldLabel
5257                         
5258                     },
5259                     
5260                     inputblock
5261                     
5262                 ];
5263
5264         } else {
5265             
5266                    Roo.log(" no label && no align");
5267                 cfg.cn = [
5268                     
5269                         inputblock
5270                     
5271                 ];
5272                 
5273                 
5274         }
5275         
5276         if (this.disabled) {
5277             input.disabled=true;
5278         }
5279         
5280         return cfg;
5281         
5282     },
5283     /**
5284      * return the real textarea element.
5285      */
5286     inputEl: function ()
5287     {
5288         return this.el.select('textarea.form-control',true).first();
5289     }
5290 });
5291
5292  
5293 /*
5294  * - LGPL
5295  *
5296  * trigger field - base class for combo..
5297  * 
5298  */
5299  
5300 /**
5301  * @class Roo.bootstrap.TriggerField
5302  * @extends Roo.bootstrap.Input
5303  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5304  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5305  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5306  * for which you can provide a custom implementation.  For example:
5307  * <pre><code>
5308 var trigger = new Roo.bootstrap.TriggerField();
5309 trigger.onTriggerClick = myTriggerFn;
5310 trigger.applyTo('my-field');
5311 </code></pre>
5312  *
5313  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5314  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5315  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5316  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5317  * @constructor
5318  * Create a new TriggerField.
5319  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5320  * to the base TextField)
5321  */
5322 Roo.bootstrap.TriggerField = function(config){
5323     this.mimicing = false;
5324     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5325 };
5326
5327 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5328     /**
5329      * @cfg {String} triggerClass A CSS class to apply to the trigger
5330      */
5331      /**
5332      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5333      */
5334     hideTrigger:false,
5335
5336     /** @cfg {Boolean} grow @hide */
5337     /** @cfg {Number} growMin @hide */
5338     /** @cfg {Number} growMax @hide */
5339
5340     /**
5341      * @hide 
5342      * @method
5343      */
5344     autoSize: Roo.emptyFn,
5345     // private
5346     monitorTab : true,
5347     // private
5348     deferHeight : true,
5349
5350     
5351     actionMode : 'wrap',
5352     
5353     
5354     
5355     getAutoCreate : function(){
5356        
5357         var parent = this.parent();
5358         
5359         var align = this.parentLabelAlign();
5360         
5361         var id = Roo.id();
5362         
5363         var cfg = {
5364             cls: 'form-group' //input-group
5365         };
5366         
5367         
5368         var input =  {
5369             tag: 'input',
5370             id : id,
5371             type : this.inputType,
5372             cls : 'form-control',
5373             autocomplete: 'off',
5374             placeholder : this.placeholder || '' 
5375             
5376         };
5377         if (this.name) {
5378             input.name = this.name;
5379         }
5380         if (this.size) {
5381             input.cls += ' input-' + this.size;
5382         }
5383         
5384         if (this.disabled) {
5385             input.disabled=true;
5386         }
5387         
5388         var inputblock = input;
5389         
5390         if (this.before || this.after) {
5391             
5392             inputblock = {
5393                 cls : 'input-group',
5394                 cn :  [] 
5395             };
5396             if (this.before) {
5397                 inputblock.cn.push({
5398                     tag :'span',
5399                     cls : 'input-group-addon',
5400                     html : this.before
5401                 });
5402             }
5403             inputblock.cn.push(input);
5404             if (this.after) {
5405                 inputblock.cn.push({
5406                     tag :'span',
5407                     cls : 'input-group-addon',
5408                     html : this.after
5409                 });
5410             }
5411             
5412         };
5413         
5414         var box = {
5415             tag: 'div',
5416             cn: [
5417                 {
5418                     tag: 'input',
5419                     type : 'hidden',
5420                     cls: 'form-hidden-field'
5421                 },
5422                 inputblock
5423             ]
5424             
5425         };
5426         
5427         if(this.multiple){
5428             Roo.log('multiple');
5429             
5430             box = {
5431                 tag: 'div',
5432                 cn: [
5433                     {
5434                         tag: 'input',
5435                         type : 'hidden',
5436                         cls: 'form-hidden-field'
5437                     },
5438                     {
5439                         tag: 'ul',
5440                         cls: 'select2-choices',
5441                         cn:[
5442                             {
5443                                 tag: 'li',
5444                                 cls: 'select2-search-field',
5445                                 cn: [
5446
5447                                     inputblock
5448                                 ]
5449                             }
5450                         ]
5451                     }
5452                 ]
5453             }
5454         };
5455         
5456         var combobox = {
5457             cls: 'select2-container input-group',
5458             cn: [
5459                 box,
5460                 {
5461                     tag: 'ul',
5462                     cls: 'typeahead typeahead-long dropdown-menu',
5463                     style: 'display:none'
5464                 }
5465             ]
5466         };
5467         
5468         if(!this.multiple){
5469             combobox.cn.push({
5470                 tag :'span',
5471                 cls : 'input-group-addon btn dropdown-toggle',
5472                 cn : [
5473                     {
5474                         tag: 'span',
5475                         cls: 'caret'
5476                     },
5477                     {
5478                         tag: 'span',
5479                         cls: 'combobox-clear',
5480                         cn  : [
5481                             {
5482                                 tag : 'i',
5483                                 cls: 'icon-remove'
5484                             }
5485                         ]
5486                     }
5487                 ]
5488
5489             })
5490         }
5491         
5492         if(this.multiple){
5493             combobox.cls += ' select2-container-multi';
5494         }
5495         
5496         if (align ==='left' && this.fieldLabel.length) {
5497             
5498                 Roo.log("left and has label");
5499                 cfg.cn = [
5500                     
5501                     {
5502                         tag: 'label',
5503                         'for' :  id,
5504                         cls : 'control-label col-sm-' + this.labelWidth,
5505                         html : this.fieldLabel
5506                         
5507                     },
5508                     {
5509                         cls : "col-sm-" + (12 - this.labelWidth), 
5510                         cn: [
5511                             combobox
5512                         ]
5513                     }
5514                     
5515                 ];
5516         } else if ( this.fieldLabel.length) {
5517                 Roo.log(" label");
5518                  cfg.cn = [
5519                    
5520                     {
5521                         tag: 'label',
5522                         //cls : 'input-group-addon',
5523                         html : this.fieldLabel
5524                         
5525                     },
5526                     
5527                     combobox
5528                     
5529                 ];
5530
5531         } else {
5532             
5533                 Roo.log(" no label && no align");
5534                 cfg = combobox
5535                      
5536                 
5537         }
5538          
5539         var settings=this;
5540         ['xs','sm','md','lg'].map(function(size){
5541             if (settings[size]) {
5542                 cfg.cls += ' col-' + size + '-' + settings[size];
5543             }
5544         });
5545         
5546         return cfg;
5547         
5548     },
5549     
5550     
5551     
5552     // private
5553     onResize : function(w, h){
5554 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5555 //        if(typeof w == 'number'){
5556 //            var x = w - this.trigger.getWidth();
5557 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5558 //            this.trigger.setStyle('left', x+'px');
5559 //        }
5560     },
5561
5562     // private
5563     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5564
5565     // private
5566     getResizeEl : function(){
5567         return this.inputEl();
5568     },
5569
5570     // private
5571     getPositionEl : function(){
5572         return this.inputEl();
5573     },
5574
5575     // private
5576     alignErrorIcon : function(){
5577         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5578     },
5579
5580     // private
5581     initEvents : function(){
5582         
5583         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5584         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5585         if(!this.multiple){
5586             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5587             if(this.hideTrigger){
5588                 this.trigger.setDisplayed(false);
5589             }
5590             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5591         }
5592         
5593         if(this.multiple){
5594             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5595         }
5596         
5597         //this.trigger.addClassOnOver('x-form-trigger-over');
5598         //this.trigger.addClassOnClick('x-form-trigger-click');
5599         
5600         //if(!this.width){
5601         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5602         //}
5603     },
5604
5605     // private
5606     initTrigger : function(){
5607        
5608     },
5609
5610     // private
5611     onDestroy : function(){
5612         if(this.trigger){
5613             this.trigger.removeAllListeners();
5614           //  this.trigger.remove();
5615         }
5616         //if(this.wrap){
5617         //    this.wrap.remove();
5618         //}
5619         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5620     },
5621
5622     // private
5623     onFocus : function(){
5624         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5625         /*
5626         if(!this.mimicing){
5627             this.wrap.addClass('x-trigger-wrap-focus');
5628             this.mimicing = true;
5629             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5630             if(this.monitorTab){
5631                 this.el.on("keydown", this.checkTab, this);
5632             }
5633         }
5634         */
5635     },
5636
5637     // private
5638     checkTab : function(e){
5639         if(e.getKey() == e.TAB){
5640             this.triggerBlur();
5641         }
5642     },
5643
5644     // private
5645     onBlur : function(){
5646         // do nothing
5647     },
5648
5649     // private
5650     mimicBlur : function(e, t){
5651         /*
5652         if(!this.wrap.contains(t) && this.validateBlur()){
5653             this.triggerBlur();
5654         }
5655         */
5656     },
5657
5658     // private
5659     triggerBlur : function(){
5660         this.mimicing = false;
5661         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5662         if(this.monitorTab){
5663             this.el.un("keydown", this.checkTab, this);
5664         }
5665         //this.wrap.removeClass('x-trigger-wrap-focus');
5666         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5667     },
5668
5669     // private
5670     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5671     validateBlur : function(e, t){
5672         return true;
5673     },
5674
5675     // private
5676     onDisable : function(){
5677         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5678         //if(this.wrap){
5679         //    this.wrap.addClass('x-item-disabled');
5680         //}
5681     },
5682
5683     // private
5684     onEnable : function(){
5685         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5686         //if(this.wrap){
5687         //    this.el.removeClass('x-item-disabled');
5688         //}
5689     },
5690
5691     // private
5692     onShow : function(){
5693         var ae = this.getActionEl();
5694         
5695         if(ae){
5696             ae.dom.style.display = '';
5697             ae.dom.style.visibility = 'visible';
5698         }
5699     },
5700
5701     // private
5702     
5703     onHide : function(){
5704         var ae = this.getActionEl();
5705         ae.dom.style.display = 'none';
5706     },
5707
5708     /**
5709      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5710      * by an implementing function.
5711      * @method
5712      * @param {EventObject} e
5713      */
5714     onTriggerClick : Roo.emptyFn
5715 });
5716  /*
5717  * Based on:
5718  * Ext JS Library 1.1.1
5719  * Copyright(c) 2006-2007, Ext JS, LLC.
5720  *
5721  * Originally Released Under LGPL - original licence link has changed is not relivant.
5722  *
5723  * Fork - LGPL
5724  * <script type="text/javascript">
5725  */
5726
5727
5728 /**
5729  * @class Roo.data.SortTypes
5730  * @singleton
5731  * Defines the default sorting (casting?) comparison functions used when sorting data.
5732  */
5733 Roo.data.SortTypes = {
5734     /**
5735      * Default sort that does nothing
5736      * @param {Mixed} s The value being converted
5737      * @return {Mixed} The comparison value
5738      */
5739     none : function(s){
5740         return s;
5741     },
5742     
5743     /**
5744      * The regular expression used to strip tags
5745      * @type {RegExp}
5746      * @property
5747      */
5748     stripTagsRE : /<\/?[^>]+>/gi,
5749     
5750     /**
5751      * Strips all HTML tags to sort on text only
5752      * @param {Mixed} s The value being converted
5753      * @return {String} The comparison value
5754      */
5755     asText : function(s){
5756         return String(s).replace(this.stripTagsRE, "");
5757     },
5758     
5759     /**
5760      * Strips all HTML tags to sort on text only - Case insensitive
5761      * @param {Mixed} s The value being converted
5762      * @return {String} The comparison value
5763      */
5764     asUCText : function(s){
5765         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5766     },
5767     
5768     /**
5769      * Case insensitive string
5770      * @param {Mixed} s The value being converted
5771      * @return {String} The comparison value
5772      */
5773     asUCString : function(s) {
5774         return String(s).toUpperCase();
5775     },
5776     
5777     /**
5778      * Date sorting
5779      * @param {Mixed} s The value being converted
5780      * @return {Number} The comparison value
5781      */
5782     asDate : function(s) {
5783         if(!s){
5784             return 0;
5785         }
5786         if(s instanceof Date){
5787             return s.getTime();
5788         }
5789         return Date.parse(String(s));
5790     },
5791     
5792     /**
5793      * Float sorting
5794      * @param {Mixed} s The value being converted
5795      * @return {Float} The comparison value
5796      */
5797     asFloat : function(s) {
5798         var val = parseFloat(String(s).replace(/,/g, ""));
5799         if(isNaN(val)) val = 0;
5800         return val;
5801     },
5802     
5803     /**
5804      * Integer sorting
5805      * @param {Mixed} s The value being converted
5806      * @return {Number} The comparison value
5807      */
5808     asInt : function(s) {
5809         var val = parseInt(String(s).replace(/,/g, ""));
5810         if(isNaN(val)) val = 0;
5811         return val;
5812     }
5813 };/*
5814  * Based on:
5815  * Ext JS Library 1.1.1
5816  * Copyright(c) 2006-2007, Ext JS, LLC.
5817  *
5818  * Originally Released Under LGPL - original licence link has changed is not relivant.
5819  *
5820  * Fork - LGPL
5821  * <script type="text/javascript">
5822  */
5823
5824 /**
5825 * @class Roo.data.Record
5826  * Instances of this class encapsulate both record <em>definition</em> information, and record
5827  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5828  * to access Records cached in an {@link Roo.data.Store} object.<br>
5829  * <p>
5830  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5831  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5832  * objects.<br>
5833  * <p>
5834  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5835  * @constructor
5836  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5837  * {@link #create}. The parameters are the same.
5838  * @param {Array} data An associative Array of data values keyed by the field name.
5839  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5840  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5841  * not specified an integer id is generated.
5842  */
5843 Roo.data.Record = function(data, id){
5844     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5845     this.data = data;
5846 };
5847
5848 /**
5849  * Generate a constructor for a specific record layout.
5850  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5851  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5852  * Each field definition object may contain the following properties: <ul>
5853  * <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,
5854  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5855  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5856  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5857  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5858  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5859  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5860  * this may be omitted.</p></li>
5861  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5862  * <ul><li>auto (Default, implies no conversion)</li>
5863  * <li>string</li>
5864  * <li>int</li>
5865  * <li>float</li>
5866  * <li>boolean</li>
5867  * <li>date</li></ul></p></li>
5868  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5869  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5870  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5871  * by the Reader into an object that will be stored in the Record. It is passed the
5872  * following parameters:<ul>
5873  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5874  * </ul></p></li>
5875  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5876  * </ul>
5877  * <br>usage:<br><pre><code>
5878 var TopicRecord = Roo.data.Record.create(
5879     {name: 'title', mapping: 'topic_title'},
5880     {name: 'author', mapping: 'username'},
5881     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5882     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5883     {name: 'lastPoster', mapping: 'user2'},
5884     {name: 'excerpt', mapping: 'post_text'}
5885 );
5886
5887 var myNewRecord = new TopicRecord({
5888     title: 'Do my job please',
5889     author: 'noobie',
5890     totalPosts: 1,
5891     lastPost: new Date(),
5892     lastPoster: 'Animal',
5893     excerpt: 'No way dude!'
5894 });
5895 myStore.add(myNewRecord);
5896 </code></pre>
5897  * @method create
5898  * @static
5899  */
5900 Roo.data.Record.create = function(o){
5901     var f = function(){
5902         f.superclass.constructor.apply(this, arguments);
5903     };
5904     Roo.extend(f, Roo.data.Record);
5905     var p = f.prototype;
5906     p.fields = new Roo.util.MixedCollection(false, function(field){
5907         return field.name;
5908     });
5909     for(var i = 0, len = o.length; i < len; i++){
5910         p.fields.add(new Roo.data.Field(o[i]));
5911     }
5912     f.getField = function(name){
5913         return p.fields.get(name);  
5914     };
5915     return f;
5916 };
5917
5918 Roo.data.Record.AUTO_ID = 1000;
5919 Roo.data.Record.EDIT = 'edit';
5920 Roo.data.Record.REJECT = 'reject';
5921 Roo.data.Record.COMMIT = 'commit';
5922
5923 Roo.data.Record.prototype = {
5924     /**
5925      * Readonly flag - true if this record has been modified.
5926      * @type Boolean
5927      */
5928     dirty : false,
5929     editing : false,
5930     error: null,
5931     modified: null,
5932
5933     // private
5934     join : function(store){
5935         this.store = store;
5936     },
5937
5938     /**
5939      * Set the named field to the specified value.
5940      * @param {String} name The name of the field to set.
5941      * @param {Object} value The value to set the field to.
5942      */
5943     set : function(name, value){
5944         if(this.data[name] == value){
5945             return;
5946         }
5947         this.dirty = true;
5948         if(!this.modified){
5949             this.modified = {};
5950         }
5951         if(typeof this.modified[name] == 'undefined'){
5952             this.modified[name] = this.data[name];
5953         }
5954         this.data[name] = value;
5955         if(!this.editing && this.store){
5956             this.store.afterEdit(this);
5957         }       
5958     },
5959
5960     /**
5961      * Get the value of the named field.
5962      * @param {String} name The name of the field to get the value of.
5963      * @return {Object} The value of the field.
5964      */
5965     get : function(name){
5966         return this.data[name]; 
5967     },
5968
5969     // private
5970     beginEdit : function(){
5971         this.editing = true;
5972         this.modified = {}; 
5973     },
5974
5975     // private
5976     cancelEdit : function(){
5977         this.editing = false;
5978         delete this.modified;
5979     },
5980
5981     // private
5982     endEdit : function(){
5983         this.editing = false;
5984         if(this.dirty && this.store){
5985             this.store.afterEdit(this);
5986         }
5987     },
5988
5989     /**
5990      * Usually called by the {@link Roo.data.Store} which owns the Record.
5991      * Rejects all changes made to the Record since either creation, or the last commit operation.
5992      * Modified fields are reverted to their original values.
5993      * <p>
5994      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5995      * of reject operations.
5996      */
5997     reject : function(){
5998         var m = this.modified;
5999         for(var n in m){
6000             if(typeof m[n] != "function"){
6001                 this.data[n] = m[n];
6002             }
6003         }
6004         this.dirty = false;
6005         delete this.modified;
6006         this.editing = false;
6007         if(this.store){
6008             this.store.afterReject(this);
6009         }
6010     },
6011
6012     /**
6013      * Usually called by the {@link Roo.data.Store} which owns the Record.
6014      * Commits all changes made to the Record since either creation, or the last commit operation.
6015      * <p>
6016      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6017      * of commit operations.
6018      */
6019     commit : function(){
6020         this.dirty = false;
6021         delete this.modified;
6022         this.editing = false;
6023         if(this.store){
6024             this.store.afterCommit(this);
6025         }
6026     },
6027
6028     // private
6029     hasError : function(){
6030         return this.error != null;
6031     },
6032
6033     // private
6034     clearError : function(){
6035         this.error = null;
6036     },
6037
6038     /**
6039      * Creates a copy of this record.
6040      * @param {String} id (optional) A new record id if you don't want to use this record's id
6041      * @return {Record}
6042      */
6043     copy : function(newId) {
6044         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6045     }
6046 };/*
6047  * Based on:
6048  * Ext JS Library 1.1.1
6049  * Copyright(c) 2006-2007, Ext JS, LLC.
6050  *
6051  * Originally Released Under LGPL - original licence link has changed is not relivant.
6052  *
6053  * Fork - LGPL
6054  * <script type="text/javascript">
6055  */
6056
6057
6058
6059 /**
6060  * @class Roo.data.Store
6061  * @extends Roo.util.Observable
6062  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6063  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6064  * <p>
6065  * 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
6066  * has no knowledge of the format of the data returned by the Proxy.<br>
6067  * <p>
6068  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6069  * instances from the data object. These records are cached and made available through accessor functions.
6070  * @constructor
6071  * Creates a new Store.
6072  * @param {Object} config A config object containing the objects needed for the Store to access data,
6073  * and read the data into Records.
6074  */
6075 Roo.data.Store = function(config){
6076     this.data = new Roo.util.MixedCollection(false);
6077     this.data.getKey = function(o){
6078         return o.id;
6079     };
6080     this.baseParams = {};
6081     // private
6082     this.paramNames = {
6083         "start" : "start",
6084         "limit" : "limit",
6085         "sort" : "sort",
6086         "dir" : "dir",
6087         "multisort" : "_multisort"
6088     };
6089
6090     if(config && config.data){
6091         this.inlineData = config.data;
6092         delete config.data;
6093     }
6094
6095     Roo.apply(this, config);
6096     
6097     if(this.reader){ // reader passed
6098         this.reader = Roo.factory(this.reader, Roo.data);
6099         this.reader.xmodule = this.xmodule || false;
6100         if(!this.recordType){
6101             this.recordType = this.reader.recordType;
6102         }
6103         if(this.reader.onMetaChange){
6104             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6105         }
6106     }
6107
6108     if(this.recordType){
6109         this.fields = this.recordType.prototype.fields;
6110     }
6111     this.modified = [];
6112
6113     this.addEvents({
6114         /**
6115          * @event datachanged
6116          * Fires when the data cache has changed, and a widget which is using this Store
6117          * as a Record cache should refresh its view.
6118          * @param {Store} this
6119          */
6120         datachanged : true,
6121         /**
6122          * @event metachange
6123          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6124          * @param {Store} this
6125          * @param {Object} meta The JSON metadata
6126          */
6127         metachange : true,
6128         /**
6129          * @event add
6130          * Fires when Records have been added to the Store
6131          * @param {Store} this
6132          * @param {Roo.data.Record[]} records The array of Records added
6133          * @param {Number} index The index at which the record(s) were added
6134          */
6135         add : true,
6136         /**
6137          * @event remove
6138          * Fires when a Record has been removed from the Store
6139          * @param {Store} this
6140          * @param {Roo.data.Record} record The Record that was removed
6141          * @param {Number} index The index at which the record was removed
6142          */
6143         remove : true,
6144         /**
6145          * @event update
6146          * Fires when a Record has been updated
6147          * @param {Store} this
6148          * @param {Roo.data.Record} record The Record that was updated
6149          * @param {String} operation The update operation being performed.  Value may be one of:
6150          * <pre><code>
6151  Roo.data.Record.EDIT
6152  Roo.data.Record.REJECT
6153  Roo.data.Record.COMMIT
6154          * </code></pre>
6155          */
6156         update : true,
6157         /**
6158          * @event clear
6159          * Fires when the data cache has been cleared.
6160          * @param {Store} this
6161          */
6162         clear : true,
6163         /**
6164          * @event beforeload
6165          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6166          * the load action will be canceled.
6167          * @param {Store} this
6168          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6169          */
6170         beforeload : true,
6171         /**
6172          * @event beforeloadadd
6173          * Fires after a new set of Records has been loaded.
6174          * @param {Store} this
6175          * @param {Roo.data.Record[]} records The Records that were loaded
6176          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6177          */
6178         beforeloadadd : true,
6179         /**
6180          * @event load
6181          * Fires after a new set of Records has been loaded, before they are added to the store.
6182          * @param {Store} this
6183          * @param {Roo.data.Record[]} records The Records that were loaded
6184          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6185          * @params {Object} return from reader
6186          */
6187         load : true,
6188         /**
6189          * @event loadexception
6190          * Fires if an exception occurs in the Proxy during loading.
6191          * Called with the signature of the Proxy's "loadexception" event.
6192          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6193          * 
6194          * @param {Proxy} 
6195          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6196          * @param {Object} load options 
6197          * @param {Object} jsonData from your request (normally this contains the Exception)
6198          */
6199         loadexception : true
6200     });
6201     
6202     if(this.proxy){
6203         this.proxy = Roo.factory(this.proxy, Roo.data);
6204         this.proxy.xmodule = this.xmodule || false;
6205         this.relayEvents(this.proxy,  ["loadexception"]);
6206     }
6207     this.sortToggle = {};
6208     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6209
6210     Roo.data.Store.superclass.constructor.call(this);
6211
6212     if(this.inlineData){
6213         this.loadData(this.inlineData);
6214         delete this.inlineData;
6215     }
6216 };
6217
6218 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6219      /**
6220     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6221     * without a remote query - used by combo/forms at present.
6222     */
6223     
6224     /**
6225     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6226     */
6227     /**
6228     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6229     */
6230     /**
6231     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6232     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6233     */
6234     /**
6235     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6236     * on any HTTP request
6237     */
6238     /**
6239     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6240     */
6241     /**
6242     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6243     */
6244     multiSort: false,
6245     /**
6246     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6247     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6248     */
6249     remoteSort : false,
6250
6251     /**
6252     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6253      * loaded or when a record is removed. (defaults to false).
6254     */
6255     pruneModifiedRecords : false,
6256
6257     // private
6258     lastOptions : null,
6259
6260     /**
6261      * Add Records to the Store and fires the add event.
6262      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6263      */
6264     add : function(records){
6265         records = [].concat(records);
6266         for(var i = 0, len = records.length; i < len; i++){
6267             records[i].join(this);
6268         }
6269         var index = this.data.length;
6270         this.data.addAll(records);
6271         this.fireEvent("add", this, records, index);
6272     },
6273
6274     /**
6275      * Remove a Record from the Store and fires the remove event.
6276      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6277      */
6278     remove : function(record){
6279         var index = this.data.indexOf(record);
6280         this.data.removeAt(index);
6281         if(this.pruneModifiedRecords){
6282             this.modified.remove(record);
6283         }
6284         this.fireEvent("remove", this, record, index);
6285     },
6286
6287     /**
6288      * Remove all Records from the Store and fires the clear event.
6289      */
6290     removeAll : function(){
6291         this.data.clear();
6292         if(this.pruneModifiedRecords){
6293             this.modified = [];
6294         }
6295         this.fireEvent("clear", this);
6296     },
6297
6298     /**
6299      * Inserts Records to the Store at the given index and fires the add event.
6300      * @param {Number} index The start index at which to insert the passed Records.
6301      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6302      */
6303     insert : function(index, records){
6304         records = [].concat(records);
6305         for(var i = 0, len = records.length; i < len; i++){
6306             this.data.insert(index, records[i]);
6307             records[i].join(this);
6308         }
6309         this.fireEvent("add", this, records, index);
6310     },
6311
6312     /**
6313      * Get the index within the cache of the passed Record.
6314      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6315      * @return {Number} The index of the passed Record. Returns -1 if not found.
6316      */
6317     indexOf : function(record){
6318         return this.data.indexOf(record);
6319     },
6320
6321     /**
6322      * Get the index within the cache of the Record with the passed id.
6323      * @param {String} id The id of the Record to find.
6324      * @return {Number} The index of the Record. Returns -1 if not found.
6325      */
6326     indexOfId : function(id){
6327         return this.data.indexOfKey(id);
6328     },
6329
6330     /**
6331      * Get the Record with the specified id.
6332      * @param {String} id The id of the Record to find.
6333      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6334      */
6335     getById : function(id){
6336         return this.data.key(id);
6337     },
6338
6339     /**
6340      * Get the Record at the specified index.
6341      * @param {Number} index The index of the Record to find.
6342      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6343      */
6344     getAt : function(index){
6345         return this.data.itemAt(index);
6346     },
6347
6348     /**
6349      * Returns a range of Records between specified indices.
6350      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6351      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6352      * @return {Roo.data.Record[]} An array of Records
6353      */
6354     getRange : function(start, end){
6355         return this.data.getRange(start, end);
6356     },
6357
6358     // private
6359     storeOptions : function(o){
6360         o = Roo.apply({}, o);
6361         delete o.callback;
6362         delete o.scope;
6363         this.lastOptions = o;
6364     },
6365
6366     /**
6367      * Loads the Record cache from the configured Proxy using the configured Reader.
6368      * <p>
6369      * If using remote paging, then the first load call must specify the <em>start</em>
6370      * and <em>limit</em> properties in the options.params property to establish the initial
6371      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6372      * <p>
6373      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6374      * and this call will return before the new data has been loaded. Perform any post-processing
6375      * in a callback function, or in a "load" event handler.</strong>
6376      * <p>
6377      * @param {Object} options An object containing properties which control loading options:<ul>
6378      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6379      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6380      * passed the following arguments:<ul>
6381      * <li>r : Roo.data.Record[]</li>
6382      * <li>options: Options object from the load call</li>
6383      * <li>success: Boolean success indicator</li></ul></li>
6384      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6385      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6386      * </ul>
6387      */
6388     load : function(options){
6389         options = options || {};
6390         if(this.fireEvent("beforeload", this, options) !== false){
6391             this.storeOptions(options);
6392             var p = Roo.apply(options.params || {}, this.baseParams);
6393             // if meta was not loaded from remote source.. try requesting it.
6394             if (!this.reader.metaFromRemote) {
6395                 p._requestMeta = 1;
6396             }
6397             if(this.sortInfo && this.remoteSort){
6398                 var pn = this.paramNames;
6399                 p[pn["sort"]] = this.sortInfo.field;
6400                 p[pn["dir"]] = this.sortInfo.direction;
6401             }
6402             if (this.multiSort) {
6403                 var pn = this.paramNames;
6404                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6405             }
6406             
6407             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6408         }
6409     },
6410
6411     /**
6412      * Reloads the Record cache from the configured Proxy using the configured Reader and
6413      * the options from the last load operation performed.
6414      * @param {Object} options (optional) An object containing properties which may override the options
6415      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6416      * the most recently used options are reused).
6417      */
6418     reload : function(options){
6419         this.load(Roo.applyIf(options||{}, this.lastOptions));
6420     },
6421
6422     // private
6423     // Called as a callback by the Reader during a load operation.
6424     loadRecords : function(o, options, success){
6425         if(!o || success === false){
6426             if(success !== false){
6427                 this.fireEvent("load", this, [], options, o);
6428             }
6429             if(options.callback){
6430                 options.callback.call(options.scope || this, [], options, false);
6431             }
6432             return;
6433         }
6434         // if data returned failure - throw an exception.
6435         if (o.success === false) {
6436             // show a message if no listener is registered.
6437             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6438                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6439             }
6440             // loadmask wil be hooked into this..
6441             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6442             return;
6443         }
6444         var r = o.records, t = o.totalRecords || r.length;
6445         
6446         this.fireEvent("beforeloadadd", this, r, options, o);
6447         
6448         if(!options || options.add !== true){
6449             if(this.pruneModifiedRecords){
6450                 this.modified = [];
6451             }
6452             for(var i = 0, len = r.length; i < len; i++){
6453                 r[i].join(this);
6454             }
6455             if(this.snapshot){
6456                 this.data = this.snapshot;
6457                 delete this.snapshot;
6458             }
6459             this.data.clear();
6460             this.data.addAll(r);
6461             this.totalLength = t;
6462             this.applySort();
6463             this.fireEvent("datachanged", this);
6464         }else{
6465             this.totalLength = Math.max(t, this.data.length+r.length);
6466             this.add(r);
6467         }
6468         this.fireEvent("load", this, r, options, o);
6469         if(options.callback){
6470             options.callback.call(options.scope || this, r, options, true);
6471         }
6472     },
6473
6474
6475     /**
6476      * Loads data from a passed data block. A Reader which understands the format of the data
6477      * must have been configured in the constructor.
6478      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6479      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6480      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6481      */
6482     loadData : function(o, append){
6483         var r = this.reader.readRecords(o);
6484         this.loadRecords(r, {add: append}, true);
6485     },
6486
6487     /**
6488      * Gets the number of cached records.
6489      * <p>
6490      * <em>If using paging, this may not be the total size of the dataset. If the data object
6491      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6492      * the data set size</em>
6493      */
6494     getCount : function(){
6495         return this.data.length || 0;
6496     },
6497
6498     /**
6499      * Gets the total number of records in the dataset as returned by the server.
6500      * <p>
6501      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6502      * the dataset size</em>
6503      */
6504     getTotalCount : function(){
6505         return this.totalLength || 0;
6506     },
6507
6508     /**
6509      * Returns the sort state of the Store as an object with two properties:
6510      * <pre><code>
6511  field {String} The name of the field by which the Records are sorted
6512  direction {String} The sort order, "ASC" or "DESC"
6513      * </code></pre>
6514      */
6515     getSortState : function(){
6516         return this.sortInfo;
6517     },
6518
6519     // private
6520     applySort : function(){
6521         if(this.sortInfo && !this.remoteSort){
6522             var s = this.sortInfo, f = s.field;
6523             var st = this.fields.get(f).sortType;
6524             var fn = function(r1, r2){
6525                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6526                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6527             };
6528             this.data.sort(s.direction, fn);
6529             if(this.snapshot && this.snapshot != this.data){
6530                 this.snapshot.sort(s.direction, fn);
6531             }
6532         }
6533     },
6534
6535     /**
6536      * Sets the default sort column and order to be used by the next load operation.
6537      * @param {String} fieldName The name of the field to sort by.
6538      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6539      */
6540     setDefaultSort : function(field, dir){
6541         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6542     },
6543
6544     /**
6545      * Sort the Records.
6546      * If remote sorting is used, the sort is performed on the server, and the cache is
6547      * reloaded. If local sorting is used, the cache is sorted internally.
6548      * @param {String} fieldName The name of the field to sort by.
6549      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6550      */
6551     sort : function(fieldName, dir){
6552         var f = this.fields.get(fieldName);
6553         if(!dir){
6554             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6555             
6556             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6557                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6558             }else{
6559                 dir = f.sortDir;
6560             }
6561         }
6562         this.sortToggle[f.name] = dir;
6563         this.sortInfo = {field: f.name, direction: dir};
6564         if(!this.remoteSort){
6565             this.applySort();
6566             this.fireEvent("datachanged", this);
6567         }else{
6568             this.load(this.lastOptions);
6569         }
6570     },
6571
6572     /**
6573      * Calls the specified function for each of the Records in the cache.
6574      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6575      * Returning <em>false</em> aborts and exits the iteration.
6576      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6577      */
6578     each : function(fn, scope){
6579         this.data.each(fn, scope);
6580     },
6581
6582     /**
6583      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6584      * (e.g., during paging).
6585      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6586      */
6587     getModifiedRecords : function(){
6588         return this.modified;
6589     },
6590
6591     // private
6592     createFilterFn : function(property, value, anyMatch){
6593         if(!value.exec){ // not a regex
6594             value = String(value);
6595             if(value.length == 0){
6596                 return false;
6597             }
6598             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6599         }
6600         return function(r){
6601             return value.test(r.data[property]);
6602         };
6603     },
6604
6605     /**
6606      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6607      * @param {String} property A field on your records
6608      * @param {Number} start The record index to start at (defaults to 0)
6609      * @param {Number} end The last record index to include (defaults to length - 1)
6610      * @return {Number} The sum
6611      */
6612     sum : function(property, start, end){
6613         var rs = this.data.items, v = 0;
6614         start = start || 0;
6615         end = (end || end === 0) ? end : rs.length-1;
6616
6617         for(var i = start; i <= end; i++){
6618             v += (rs[i].data[property] || 0);
6619         }
6620         return v;
6621     },
6622
6623     /**
6624      * Filter 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      */
6630     filter : function(property, value, anyMatch){
6631         var fn = this.createFilterFn(property, value, anyMatch);
6632         return fn ? this.filterBy(fn) : this.clearFilter();
6633     },
6634
6635     /**
6636      * Filter by a function. The specified function will be called with each
6637      * record in this data source. If the function returns true the record is included,
6638      * otherwise it is filtered.
6639      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6640      * @param {Object} scope (optional) The scope of the function (defaults to this)
6641      */
6642     filterBy : function(fn, scope){
6643         this.snapshot = this.snapshot || this.data;
6644         this.data = this.queryBy(fn, scope||this);
6645         this.fireEvent("datachanged", this);
6646     },
6647
6648     /**
6649      * Query the records by a specified property.
6650      * @param {String} field A field on your records
6651      * @param {String/RegExp} value Either a string that the field
6652      * should start with or a RegExp to test against the field
6653      * @param {Boolean} anyMatch True to match any part not just the beginning
6654      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6655      */
6656     query : function(property, value, anyMatch){
6657         var fn = this.createFilterFn(property, value, anyMatch);
6658         return fn ? this.queryBy(fn) : this.data.clone();
6659     },
6660
6661     /**
6662      * Query by a function. The specified function will be called with each
6663      * record in this data source. If the function returns true the record is included
6664      * in the results.
6665      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6666      * @param {Object} scope (optional) The scope of the function (defaults to this)
6667       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6668      **/
6669     queryBy : function(fn, scope){
6670         var data = this.snapshot || this.data;
6671         return data.filterBy(fn, scope||this);
6672     },
6673
6674     /**
6675      * Collects unique values for a particular dataIndex from this store.
6676      * @param {String} dataIndex The property to collect
6677      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6678      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6679      * @return {Array} An array of the unique values
6680      **/
6681     collect : function(dataIndex, allowNull, bypassFilter){
6682         var d = (bypassFilter === true && this.snapshot) ?
6683                 this.snapshot.items : this.data.items;
6684         var v, sv, r = [], l = {};
6685         for(var i = 0, len = d.length; i < len; i++){
6686             v = d[i].data[dataIndex];
6687             sv = String(v);
6688             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6689                 l[sv] = true;
6690                 r[r.length] = v;
6691             }
6692         }
6693         return r;
6694     },
6695
6696     /**
6697      * Revert to a view of the Record cache with no filtering applied.
6698      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6699      */
6700     clearFilter : function(suppressEvent){
6701         if(this.snapshot && this.snapshot != this.data){
6702             this.data = this.snapshot;
6703             delete this.snapshot;
6704             if(suppressEvent !== true){
6705                 this.fireEvent("datachanged", this);
6706             }
6707         }
6708     },
6709
6710     // private
6711     afterEdit : function(record){
6712         if(this.modified.indexOf(record) == -1){
6713             this.modified.push(record);
6714         }
6715         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6716     },
6717     
6718     // private
6719     afterReject : function(record){
6720         this.modified.remove(record);
6721         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6722     },
6723
6724     // private
6725     afterCommit : function(record){
6726         this.modified.remove(record);
6727         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6728     },
6729
6730     /**
6731      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6732      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6733      */
6734     commitChanges : function(){
6735         var m = this.modified.slice(0);
6736         this.modified = [];
6737         for(var i = 0, len = m.length; i < len; i++){
6738             m[i].commit();
6739         }
6740     },
6741
6742     /**
6743      * Cancel outstanding changes on all changed records.
6744      */
6745     rejectChanges : function(){
6746         var m = this.modified.slice(0);
6747         this.modified = [];
6748         for(var i = 0, len = m.length; i < len; i++){
6749             m[i].reject();
6750         }
6751     },
6752
6753     onMetaChange : function(meta, rtype, o){
6754         this.recordType = rtype;
6755         this.fields = rtype.prototype.fields;
6756         delete this.snapshot;
6757         this.sortInfo = meta.sortInfo || this.sortInfo;
6758         this.modified = [];
6759         this.fireEvent('metachange', this, this.reader.meta);
6760     },
6761     
6762     moveIndex : function(data, type)
6763     {
6764         var index = this.indexOf(data);
6765         
6766         var newIndex = index + type;
6767         
6768         this.remove(data);
6769         
6770         this.insert(newIndex, data);
6771         
6772     }
6773 });/*
6774  * Based on:
6775  * Ext JS Library 1.1.1
6776  * Copyright(c) 2006-2007, Ext JS, LLC.
6777  *
6778  * Originally Released Under LGPL - original licence link has changed is not relivant.
6779  *
6780  * Fork - LGPL
6781  * <script type="text/javascript">
6782  */
6783
6784 /**
6785  * @class Roo.data.SimpleStore
6786  * @extends Roo.data.Store
6787  * Small helper class to make creating Stores from Array data easier.
6788  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6789  * @cfg {Array} fields An array of field definition objects, or field name strings.
6790  * @cfg {Array} data The multi-dimensional array of data
6791  * @constructor
6792  * @param {Object} config
6793  */
6794 Roo.data.SimpleStore = function(config){
6795     Roo.data.SimpleStore.superclass.constructor.call(this, {
6796         isLocal : true,
6797         reader: new Roo.data.ArrayReader({
6798                 id: config.id
6799             },
6800             Roo.data.Record.create(config.fields)
6801         ),
6802         proxy : new Roo.data.MemoryProxy(config.data)
6803     });
6804     this.load();
6805 };
6806 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6807  * Based on:
6808  * Ext JS Library 1.1.1
6809  * Copyright(c) 2006-2007, Ext JS, LLC.
6810  *
6811  * Originally Released Under LGPL - original licence link has changed is not relivant.
6812  *
6813  * Fork - LGPL
6814  * <script type="text/javascript">
6815  */
6816
6817 /**
6818 /**
6819  * @extends Roo.data.Store
6820  * @class Roo.data.JsonStore
6821  * Small helper class to make creating Stores for JSON data easier. <br/>
6822 <pre><code>
6823 var store = new Roo.data.JsonStore({
6824     url: 'get-images.php',
6825     root: 'images',
6826     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6827 });
6828 </code></pre>
6829  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6830  * JsonReader and HttpProxy (unless inline data is provided).</b>
6831  * @cfg {Array} fields An array of field definition objects, or field name strings.
6832  * @constructor
6833  * @param {Object} config
6834  */
6835 Roo.data.JsonStore = function(c){
6836     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6837         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6838         reader: new Roo.data.JsonReader(c, c.fields)
6839     }));
6840 };
6841 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6842  * Based on:
6843  * Ext JS Library 1.1.1
6844  * Copyright(c) 2006-2007, Ext JS, LLC.
6845  *
6846  * Originally Released Under LGPL - original licence link has changed is not relivant.
6847  *
6848  * Fork - LGPL
6849  * <script type="text/javascript">
6850  */
6851
6852  
6853 Roo.data.Field = function(config){
6854     if(typeof config == "string"){
6855         config = {name: config};
6856     }
6857     Roo.apply(this, config);
6858     
6859     if(!this.type){
6860         this.type = "auto";
6861     }
6862     
6863     var st = Roo.data.SortTypes;
6864     // named sortTypes are supported, here we look them up
6865     if(typeof this.sortType == "string"){
6866         this.sortType = st[this.sortType];
6867     }
6868     
6869     // set default sortType for strings and dates
6870     if(!this.sortType){
6871         switch(this.type){
6872             case "string":
6873                 this.sortType = st.asUCString;
6874                 break;
6875             case "date":
6876                 this.sortType = st.asDate;
6877                 break;
6878             default:
6879                 this.sortType = st.none;
6880         }
6881     }
6882
6883     // define once
6884     var stripRe = /[\$,%]/g;
6885
6886     // prebuilt conversion function for this field, instead of
6887     // switching every time we're reading a value
6888     if(!this.convert){
6889         var cv, dateFormat = this.dateFormat;
6890         switch(this.type){
6891             case "":
6892             case "auto":
6893             case undefined:
6894                 cv = function(v){ return v; };
6895                 break;
6896             case "string":
6897                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6898                 break;
6899             case "int":
6900                 cv = function(v){
6901                     return v !== undefined && v !== null && v !== '' ?
6902                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6903                     };
6904                 break;
6905             case "float":
6906                 cv = function(v){
6907                     return v !== undefined && v !== null && v !== '' ?
6908                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6909                     };
6910                 break;
6911             case "bool":
6912             case "boolean":
6913                 cv = function(v){ return v === true || v === "true" || v == 1; };
6914                 break;
6915             case "date":
6916                 cv = function(v){
6917                     if(!v){
6918                         return '';
6919                     }
6920                     if(v instanceof Date){
6921                         return v;
6922                     }
6923                     if(dateFormat){
6924                         if(dateFormat == "timestamp"){
6925                             return new Date(v*1000);
6926                         }
6927                         return Date.parseDate(v, dateFormat);
6928                     }
6929                     var parsed = Date.parse(v);
6930                     return parsed ? new Date(parsed) : null;
6931                 };
6932              break;
6933             
6934         }
6935         this.convert = cv;
6936     }
6937 };
6938
6939 Roo.data.Field.prototype = {
6940     dateFormat: null,
6941     defaultValue: "",
6942     mapping: null,
6943     sortType : null,
6944     sortDir : "ASC"
6945 };/*
6946  * Based on:
6947  * Ext JS Library 1.1.1
6948  * Copyright(c) 2006-2007, Ext JS, LLC.
6949  *
6950  * Originally Released Under LGPL - original licence link has changed is not relivant.
6951  *
6952  * Fork - LGPL
6953  * <script type="text/javascript">
6954  */
6955  
6956 // Base class for reading structured data from a data source.  This class is intended to be
6957 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6958
6959 /**
6960  * @class Roo.data.DataReader
6961  * Base class for reading structured data from a data source.  This class is intended to be
6962  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6963  */
6964
6965 Roo.data.DataReader = function(meta, recordType){
6966     
6967     this.meta = meta;
6968     
6969     this.recordType = recordType instanceof Array ? 
6970         Roo.data.Record.create(recordType) : recordType;
6971 };
6972
6973 Roo.data.DataReader.prototype = {
6974      /**
6975      * Create an empty record
6976      * @param {Object} data (optional) - overlay some values
6977      * @return {Roo.data.Record} record created.
6978      */
6979     newRow :  function(d) {
6980         var da =  {};
6981         this.recordType.prototype.fields.each(function(c) {
6982             switch( c.type) {
6983                 case 'int' : da[c.name] = 0; break;
6984                 case 'date' : da[c.name] = new Date(); break;
6985                 case 'float' : da[c.name] = 0.0; break;
6986                 case 'boolean' : da[c.name] = false; break;
6987                 default : da[c.name] = ""; break;
6988             }
6989             
6990         });
6991         return new this.recordType(Roo.apply(da, d));
6992     }
6993     
6994 };/*
6995  * Based on:
6996  * Ext JS Library 1.1.1
6997  * Copyright(c) 2006-2007, Ext JS, LLC.
6998  *
6999  * Originally Released Under LGPL - original licence link has changed is not relivant.
7000  *
7001  * Fork - LGPL
7002  * <script type="text/javascript">
7003  */
7004
7005 /**
7006  * @class Roo.data.DataProxy
7007  * @extends Roo.data.Observable
7008  * This class is an abstract base class for implementations which provide retrieval of
7009  * unformatted data objects.<br>
7010  * <p>
7011  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7012  * (of the appropriate type which knows how to parse the data object) to provide a block of
7013  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7014  * <p>
7015  * Custom implementations must implement the load method as described in
7016  * {@link Roo.data.HttpProxy#load}.
7017  */
7018 Roo.data.DataProxy = function(){
7019     this.addEvents({
7020         /**
7021          * @event beforeload
7022          * Fires before a network request is made to retrieve a data object.
7023          * @param {Object} This DataProxy object.
7024          * @param {Object} params The params parameter to the load function.
7025          */
7026         beforeload : true,
7027         /**
7028          * @event load
7029          * Fires before the load method's callback is called.
7030          * @param {Object} This DataProxy object.
7031          * @param {Object} o The data object.
7032          * @param {Object} arg The callback argument object passed to the load function.
7033          */
7034         load : true,
7035         /**
7036          * @event loadexception
7037          * Fires if an Exception occurs during data retrieval.
7038          * @param {Object} This DataProxy object.
7039          * @param {Object} o The data object.
7040          * @param {Object} arg The callback argument object passed to the load function.
7041          * @param {Object} e The Exception.
7042          */
7043         loadexception : true
7044     });
7045     Roo.data.DataProxy.superclass.constructor.call(this);
7046 };
7047
7048 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7049
7050     /**
7051      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7052      */
7053 /*
7054  * Based on:
7055  * Ext JS Library 1.1.1
7056  * Copyright(c) 2006-2007, Ext JS, LLC.
7057  *
7058  * Originally Released Under LGPL - original licence link has changed is not relivant.
7059  *
7060  * Fork - LGPL
7061  * <script type="text/javascript">
7062  */
7063 /**
7064  * @class Roo.data.MemoryProxy
7065  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7066  * to the Reader when its load method is called.
7067  * @constructor
7068  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7069  */
7070 Roo.data.MemoryProxy = function(data){
7071     if (data.data) {
7072         data = data.data;
7073     }
7074     Roo.data.MemoryProxy.superclass.constructor.call(this);
7075     this.data = data;
7076 };
7077
7078 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7079     /**
7080      * Load data from the requested source (in this case an in-memory
7081      * data object passed to the constructor), read the data object into
7082      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7083      * process that block using the passed callback.
7084      * @param {Object} params This parameter is not used by the MemoryProxy class.
7085      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7086      * object into a block of Roo.data.Records.
7087      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7088      * The function must be passed <ul>
7089      * <li>The Record block object</li>
7090      * <li>The "arg" argument from the load function</li>
7091      * <li>A boolean success indicator</li>
7092      * </ul>
7093      * @param {Object} scope The scope in which to call the callback
7094      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7095      */
7096     load : function(params, reader, callback, scope, arg){
7097         params = params || {};
7098         var result;
7099         try {
7100             result = reader.readRecords(this.data);
7101         }catch(e){
7102             this.fireEvent("loadexception", this, arg, null, e);
7103             callback.call(scope, null, arg, false);
7104             return;
7105         }
7106         callback.call(scope, result, arg, true);
7107     },
7108     
7109     // private
7110     update : function(params, records){
7111         
7112     }
7113 });/*
7114  * Based on:
7115  * Ext JS Library 1.1.1
7116  * Copyright(c) 2006-2007, Ext JS, LLC.
7117  *
7118  * Originally Released Under LGPL - original licence link has changed is not relivant.
7119  *
7120  * Fork - LGPL
7121  * <script type="text/javascript">
7122  */
7123 /**
7124  * @class Roo.data.HttpProxy
7125  * @extends Roo.data.DataProxy
7126  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7127  * configured to reference a certain URL.<br><br>
7128  * <p>
7129  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7130  * from which the running page was served.<br><br>
7131  * <p>
7132  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7133  * <p>
7134  * Be aware that to enable the browser to parse an XML document, the server must set
7135  * the Content-Type header in the HTTP response to "text/xml".
7136  * @constructor
7137  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7138  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7139  * will be used to make the request.
7140  */
7141 Roo.data.HttpProxy = function(conn){
7142     Roo.data.HttpProxy.superclass.constructor.call(this);
7143     // is conn a conn config or a real conn?
7144     this.conn = conn;
7145     this.useAjax = !conn || !conn.events;
7146   
7147 };
7148
7149 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7150     // thse are take from connection...
7151     
7152     /**
7153      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7154      */
7155     /**
7156      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7157      * extra parameters to each request made by this object. (defaults to undefined)
7158      */
7159     /**
7160      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7161      *  to each request made by this object. (defaults to undefined)
7162      */
7163     /**
7164      * @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)
7165      */
7166     /**
7167      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7168      */
7169      /**
7170      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7171      * @type Boolean
7172      */
7173   
7174
7175     /**
7176      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7177      * @type Boolean
7178      */
7179     /**
7180      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7181      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7182      * a finer-grained basis than the DataProxy events.
7183      */
7184     getConnection : function(){
7185         return this.useAjax ? Roo.Ajax : this.conn;
7186     },
7187
7188     /**
7189      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7190      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7191      * process that block using the passed callback.
7192      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7193      * for the request to the remote server.
7194      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7195      * object into a block of Roo.data.Records.
7196      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7197      * The function must be passed <ul>
7198      * <li>The Record block object</li>
7199      * <li>The "arg" argument from the load function</li>
7200      * <li>A boolean success indicator</li>
7201      * </ul>
7202      * @param {Object} scope The scope in which to call the callback
7203      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7204      */
7205     load : function(params, reader, callback, scope, arg){
7206         if(this.fireEvent("beforeload", this, params) !== false){
7207             var  o = {
7208                 params : params || {},
7209                 request: {
7210                     callback : callback,
7211                     scope : scope,
7212                     arg : arg
7213                 },
7214                 reader: reader,
7215                 callback : this.loadResponse,
7216                 scope: this
7217             };
7218             if(this.useAjax){
7219                 Roo.applyIf(o, this.conn);
7220                 if(this.activeRequest){
7221                     Roo.Ajax.abort(this.activeRequest);
7222                 }
7223                 this.activeRequest = Roo.Ajax.request(o);
7224             }else{
7225                 this.conn.request(o);
7226             }
7227         }else{
7228             callback.call(scope||this, null, arg, false);
7229         }
7230     },
7231
7232     // private
7233     loadResponse : function(o, success, response){
7234         delete this.activeRequest;
7235         if(!success){
7236             this.fireEvent("loadexception", this, o, response);
7237             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7238             return;
7239         }
7240         var result;
7241         try {
7242             result = o.reader.read(response);
7243         }catch(e){
7244             this.fireEvent("loadexception", this, o, response, e);
7245             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7246             return;
7247         }
7248         
7249         this.fireEvent("load", this, o, o.request.arg);
7250         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7251     },
7252
7253     // private
7254     update : function(dataSet){
7255
7256     },
7257
7258     // private
7259     updateResponse : function(dataSet){
7260
7261     }
7262 });/*
7263  * Based on:
7264  * Ext JS Library 1.1.1
7265  * Copyright(c) 2006-2007, Ext JS, LLC.
7266  *
7267  * Originally Released Under LGPL - original licence link has changed is not relivant.
7268  *
7269  * Fork - LGPL
7270  * <script type="text/javascript">
7271  */
7272
7273 /**
7274  * @class Roo.data.ScriptTagProxy
7275  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7276  * other than the originating domain of the running page.<br><br>
7277  * <p>
7278  * <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
7279  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7280  * <p>
7281  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7282  * source code that is used as the source inside a &lt;script> tag.<br><br>
7283  * <p>
7284  * In order for the browser to process the returned data, the server must wrap the data object
7285  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7286  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7287  * depending on whether the callback name was passed:
7288  * <p>
7289  * <pre><code>
7290 boolean scriptTag = false;
7291 String cb = request.getParameter("callback");
7292 if (cb != null) {
7293     scriptTag = true;
7294     response.setContentType("text/javascript");
7295 } else {
7296     response.setContentType("application/x-json");
7297 }
7298 Writer out = response.getWriter();
7299 if (scriptTag) {
7300     out.write(cb + "(");
7301 }
7302 out.print(dataBlock.toJsonString());
7303 if (scriptTag) {
7304     out.write(");");
7305 }
7306 </pre></code>
7307  *
7308  * @constructor
7309  * @param {Object} config A configuration object.
7310  */
7311 Roo.data.ScriptTagProxy = function(config){
7312     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7313     Roo.apply(this, config);
7314     this.head = document.getElementsByTagName("head")[0];
7315 };
7316
7317 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7318
7319 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7320     /**
7321      * @cfg {String} url The URL from which to request the data object.
7322      */
7323     /**
7324      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7325      */
7326     timeout : 30000,
7327     /**
7328      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7329      * the server the name of the callback function set up by the load call to process the returned data object.
7330      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7331      * javascript output which calls this named function passing the data object as its only parameter.
7332      */
7333     callbackParam : "callback",
7334     /**
7335      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7336      * name to the request.
7337      */
7338     nocache : true,
7339
7340     /**
7341      * Load data from the configured URL, read the data object into
7342      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7343      * process that block using the passed callback.
7344      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7345      * for the request to the remote server.
7346      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7347      * object into a block of Roo.data.Records.
7348      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7349      * The function must be passed <ul>
7350      * <li>The Record block object</li>
7351      * <li>The "arg" argument from the load function</li>
7352      * <li>A boolean success indicator</li>
7353      * </ul>
7354      * @param {Object} scope The scope in which to call the callback
7355      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7356      */
7357     load : function(params, reader, callback, scope, arg){
7358         if(this.fireEvent("beforeload", this, params) !== false){
7359
7360             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7361
7362             var url = this.url;
7363             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7364             if(this.nocache){
7365                 url += "&_dc=" + (new Date().getTime());
7366             }
7367             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7368             var trans = {
7369                 id : transId,
7370                 cb : "stcCallback"+transId,
7371                 scriptId : "stcScript"+transId,
7372                 params : params,
7373                 arg : arg,
7374                 url : url,
7375                 callback : callback,
7376                 scope : scope,
7377                 reader : reader
7378             };
7379             var conn = this;
7380
7381             window[trans.cb] = function(o){
7382                 conn.handleResponse(o, trans);
7383             };
7384
7385             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7386
7387             if(this.autoAbort !== false){
7388                 this.abort();
7389             }
7390
7391             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7392
7393             var script = document.createElement("script");
7394             script.setAttribute("src", url);
7395             script.setAttribute("type", "text/javascript");
7396             script.setAttribute("id", trans.scriptId);
7397             this.head.appendChild(script);
7398
7399             this.trans = trans;
7400         }else{
7401             callback.call(scope||this, null, arg, false);
7402         }
7403     },
7404
7405     // private
7406     isLoading : function(){
7407         return this.trans ? true : false;
7408     },
7409
7410     /**
7411      * Abort the current server request.
7412      */
7413     abort : function(){
7414         if(this.isLoading()){
7415             this.destroyTrans(this.trans);
7416         }
7417     },
7418
7419     // private
7420     destroyTrans : function(trans, isLoaded){
7421         this.head.removeChild(document.getElementById(trans.scriptId));
7422         clearTimeout(trans.timeoutId);
7423         if(isLoaded){
7424             window[trans.cb] = undefined;
7425             try{
7426                 delete window[trans.cb];
7427             }catch(e){}
7428         }else{
7429             // if hasn't been loaded, wait for load to remove it to prevent script error
7430             window[trans.cb] = function(){
7431                 window[trans.cb] = undefined;
7432                 try{
7433                     delete window[trans.cb];
7434                 }catch(e){}
7435             };
7436         }
7437     },
7438
7439     // private
7440     handleResponse : function(o, trans){
7441         this.trans = false;
7442         this.destroyTrans(trans, true);
7443         var result;
7444         try {
7445             result = trans.reader.readRecords(o);
7446         }catch(e){
7447             this.fireEvent("loadexception", this, o, trans.arg, e);
7448             trans.callback.call(trans.scope||window, null, trans.arg, false);
7449             return;
7450         }
7451         this.fireEvent("load", this, o, trans.arg);
7452         trans.callback.call(trans.scope||window, result, trans.arg, true);
7453     },
7454
7455     // private
7456     handleFailure : function(trans){
7457         this.trans = false;
7458         this.destroyTrans(trans, false);
7459         this.fireEvent("loadexception", this, null, trans.arg);
7460         trans.callback.call(trans.scope||window, null, trans.arg, false);
7461     }
7462 });/*
7463  * Based on:
7464  * Ext JS Library 1.1.1
7465  * Copyright(c) 2006-2007, Ext JS, LLC.
7466  *
7467  * Originally Released Under LGPL - original licence link has changed is not relivant.
7468  *
7469  * Fork - LGPL
7470  * <script type="text/javascript">
7471  */
7472
7473 /**
7474  * @class Roo.data.JsonReader
7475  * @extends Roo.data.DataReader
7476  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7477  * based on mappings in a provided Roo.data.Record constructor.
7478  * 
7479  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7480  * in the reply previously. 
7481  * 
7482  * <p>
7483  * Example code:
7484  * <pre><code>
7485 var RecordDef = Roo.data.Record.create([
7486     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7487     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7488 ]);
7489 var myReader = new Roo.data.JsonReader({
7490     totalProperty: "results",    // The property which contains the total dataset size (optional)
7491     root: "rows",                // The property which contains an Array of row objects
7492     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7493 }, RecordDef);
7494 </code></pre>
7495  * <p>
7496  * This would consume a JSON file like this:
7497  * <pre><code>
7498 { 'results': 2, 'rows': [
7499     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7500     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7501 }
7502 </code></pre>
7503  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7504  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7505  * paged from the remote server.
7506  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7507  * @cfg {String} root name of the property which contains the Array of row objects.
7508  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7509  * @constructor
7510  * Create a new JsonReader
7511  * @param {Object} meta Metadata configuration options
7512  * @param {Object} recordType Either an Array of field definition objects,
7513  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7514  */
7515 Roo.data.JsonReader = function(meta, recordType){
7516     
7517     meta = meta || {};
7518     // set some defaults:
7519     Roo.applyIf(meta, {
7520         totalProperty: 'total',
7521         successProperty : 'success',
7522         root : 'data',
7523         id : 'id'
7524     });
7525     
7526     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7527 };
7528 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7529     
7530     /**
7531      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7532      * Used by Store query builder to append _requestMeta to params.
7533      * 
7534      */
7535     metaFromRemote : false,
7536     /**
7537      * This method is only used by a DataProxy which has retrieved data from a remote server.
7538      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7539      * @return {Object} data A data block which is used by an Roo.data.Store object as
7540      * a cache of Roo.data.Records.
7541      */
7542     read : function(response){
7543         var json = response.responseText;
7544        
7545         var o = /* eval:var:o */ eval("("+json+")");
7546         if(!o) {
7547             throw {message: "JsonReader.read: Json object not found"};
7548         }
7549         
7550         if(o.metaData){
7551             
7552             delete this.ef;
7553             this.metaFromRemote = true;
7554             this.meta = o.metaData;
7555             this.recordType = Roo.data.Record.create(o.metaData.fields);
7556             this.onMetaChange(this.meta, this.recordType, o);
7557         }
7558         return this.readRecords(o);
7559     },
7560
7561     // private function a store will implement
7562     onMetaChange : function(meta, recordType, o){
7563
7564     },
7565
7566     /**
7567          * @ignore
7568          */
7569     simpleAccess: function(obj, subsc) {
7570         return obj[subsc];
7571     },
7572
7573         /**
7574          * @ignore
7575          */
7576     getJsonAccessor: function(){
7577         var re = /[\[\.]/;
7578         return function(expr) {
7579             try {
7580                 return(re.test(expr))
7581                     ? new Function("obj", "return obj." + expr)
7582                     : function(obj){
7583                         return obj[expr];
7584                     };
7585             } catch(e){}
7586             return Roo.emptyFn;
7587         };
7588     }(),
7589
7590     /**
7591      * Create a data block containing Roo.data.Records from an XML document.
7592      * @param {Object} o An object which contains an Array of row objects in the property specified
7593      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7594      * which contains the total size of the dataset.
7595      * @return {Object} data A data block which is used by an Roo.data.Store object as
7596      * a cache of Roo.data.Records.
7597      */
7598     readRecords : function(o){
7599         /**
7600          * After any data loads, the raw JSON data is available for further custom processing.
7601          * @type Object
7602          */
7603         this.o = o;
7604         var s = this.meta, Record = this.recordType,
7605             f = Record.prototype.fields, fi = f.items, fl = f.length;
7606
7607 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7608         if (!this.ef) {
7609             if(s.totalProperty) {
7610                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7611                 }
7612                 if(s.successProperty) {
7613                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7614                 }
7615                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7616                 if (s.id) {
7617                         var g = this.getJsonAccessor(s.id);
7618                         this.getId = function(rec) {
7619                                 var r = g(rec);
7620                                 return (r === undefined || r === "") ? null : r;
7621                         };
7622                 } else {
7623                         this.getId = function(){return null;};
7624                 }
7625             this.ef = [];
7626             for(var jj = 0; jj < fl; jj++){
7627                 f = fi[jj];
7628                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7629                 this.ef[jj] = this.getJsonAccessor(map);
7630             }
7631         }
7632
7633         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7634         if(s.totalProperty){
7635             var vt = parseInt(this.getTotal(o), 10);
7636             if(!isNaN(vt)){
7637                 totalRecords = vt;
7638             }
7639         }
7640         if(s.successProperty){
7641             var vs = this.getSuccess(o);
7642             if(vs === false || vs === 'false'){
7643                 success = false;
7644             }
7645         }
7646         var records = [];
7647             for(var i = 0; i < c; i++){
7648                     var n = root[i];
7649                 var values = {};
7650                 var id = this.getId(n);
7651                 for(var j = 0; j < fl; j++){
7652                     f = fi[j];
7653                 var v = this.ef[j](n);
7654                 if (!f.convert) {
7655                     Roo.log('missing convert for ' + f.name);
7656                     Roo.log(f);
7657                     continue;
7658                 }
7659                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7660                 }
7661                 var record = new Record(values, id);
7662                 record.json = n;
7663                 records[i] = record;
7664             }
7665             return {
7666             raw : o,
7667                 success : success,
7668                 records : records,
7669                 totalRecords : totalRecords
7670             };
7671     }
7672 });/*
7673  * Based on:
7674  * Ext JS Library 1.1.1
7675  * Copyright(c) 2006-2007, Ext JS, LLC.
7676  *
7677  * Originally Released Under LGPL - original licence link has changed is not relivant.
7678  *
7679  * Fork - LGPL
7680  * <script type="text/javascript">
7681  */
7682
7683 /**
7684  * @class Roo.data.ArrayReader
7685  * @extends Roo.data.DataReader
7686  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7687  * Each element of that Array represents a row of data fields. The
7688  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7689  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7690  * <p>
7691  * Example code:.
7692  * <pre><code>
7693 var RecordDef = Roo.data.Record.create([
7694     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7695     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7696 ]);
7697 var myReader = new Roo.data.ArrayReader({
7698     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7699 }, RecordDef);
7700 </code></pre>
7701  * <p>
7702  * This would consume an Array like this:
7703  * <pre><code>
7704 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7705   </code></pre>
7706  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7707  * @constructor
7708  * Create a new JsonReader
7709  * @param {Object} meta Metadata configuration options.
7710  * @param {Object} recordType Either an Array of field definition objects
7711  * as specified to {@link Roo.data.Record#create},
7712  * or an {@link Roo.data.Record} object
7713  * created using {@link Roo.data.Record#create}.
7714  */
7715 Roo.data.ArrayReader = function(meta, recordType){
7716     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7717 };
7718
7719 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7720     /**
7721      * Create a data block containing Roo.data.Records from an XML document.
7722      * @param {Object} o An Array of row objects which represents the dataset.
7723      * @return {Object} data A data block which is used by an Roo.data.Store object as
7724      * a cache of Roo.data.Records.
7725      */
7726     readRecords : function(o){
7727         var sid = this.meta ? this.meta.id : null;
7728         var recordType = this.recordType, fields = recordType.prototype.fields;
7729         var records = [];
7730         var root = o;
7731             for(var i = 0; i < root.length; i++){
7732                     var n = root[i];
7733                 var values = {};
7734                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7735                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7736                 var f = fields.items[j];
7737                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7738                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7739                 v = f.convert(v);
7740                 values[f.name] = v;
7741             }
7742                 var record = new recordType(values, id);
7743                 record.json = n;
7744                 records[records.length] = record;
7745             }
7746             return {
7747                 records : records,
7748                 totalRecords : records.length
7749             };
7750     }
7751 });/*
7752  * - LGPL
7753  * * 
7754  */
7755
7756 /**
7757  * @class Roo.bootstrap.ComboBox
7758  * @extends Roo.bootstrap.TriggerField
7759  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7760  * @cfg {Boolean} append (true|false) default false
7761  * @constructor
7762  * Create a new ComboBox.
7763  * @param {Object} config Configuration options
7764  */
7765 Roo.bootstrap.ComboBox = function(config){
7766     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7767     this.addEvents({
7768         /**
7769          * @event expand
7770          * Fires when the dropdown list is expanded
7771              * @param {Roo.bootstrap.ComboBox} combo This combo box
7772              */
7773         'expand' : true,
7774         /**
7775          * @event collapse
7776          * Fires when the dropdown list is collapsed
7777              * @param {Roo.bootstrap.ComboBox} combo This combo box
7778              */
7779         'collapse' : true,
7780         /**
7781          * @event beforeselect
7782          * Fires before a list item is selected. Return false to cancel the selection.
7783              * @param {Roo.bootstrap.ComboBox} combo This combo box
7784              * @param {Roo.data.Record} record The data record returned from the underlying store
7785              * @param {Number} index The index of the selected item in the dropdown list
7786              */
7787         'beforeselect' : true,
7788         /**
7789          * @event select
7790          * Fires when a list item is selected
7791              * @param {Roo.bootstrap.ComboBox} combo This combo box
7792              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7793              * @param {Number} index The index of the selected item in the dropdown list
7794              */
7795         'select' : true,
7796         /**
7797          * @event beforequery
7798          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7799          * The event object passed has these properties:
7800              * @param {Roo.bootstrap.ComboBox} combo This combo box
7801              * @param {String} query The query
7802              * @param {Boolean} forceAll true to force "all" query
7803              * @param {Boolean} cancel true to cancel the query
7804              * @param {Object} e The query event object
7805              */
7806         'beforequery': true,
7807          /**
7808          * @event add
7809          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7810              * @param {Roo.bootstrap.ComboBox} combo This combo box
7811              */
7812         'add' : true,
7813         /**
7814          * @event edit
7815          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7816              * @param {Roo.bootstrap.ComboBox} combo This combo box
7817              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7818              */
7819         'edit' : true,
7820         /**
7821          * @event remove
7822          * Fires when the remove value from the combobox array
7823              * @param {Roo.bootstrap.ComboBox} combo This combo box
7824              */
7825         'remove' : true
7826         
7827     });
7828     
7829     
7830     this.selectedIndex = -1;
7831     if(this.mode == 'local'){
7832         if(config.queryDelay === undefined){
7833             this.queryDelay = 10;
7834         }
7835         if(config.minChars === undefined){
7836             this.minChars = 0;
7837         }
7838     }
7839 };
7840
7841 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7842      
7843     /**
7844      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7845      * rendering into an Roo.Editor, defaults to false)
7846      */
7847     /**
7848      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7849      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7850      */
7851     /**
7852      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7853      */
7854     /**
7855      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7856      * the dropdown list (defaults to undefined, with no header element)
7857      */
7858
7859      /**
7860      * @cfg {String/Roo.Template} tpl The template to use to render the output
7861      */
7862      
7863      /**
7864      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7865      */
7866     listWidth: undefined,
7867     /**
7868      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7869      * mode = 'remote' or 'text' if mode = 'local')
7870      */
7871     displayField: undefined,
7872     /**
7873      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7874      * mode = 'remote' or 'value' if mode = 'local'). 
7875      * Note: use of a valueField requires the user make a selection
7876      * in order for a value to be mapped.
7877      */
7878     valueField: undefined,
7879     
7880     
7881     /**
7882      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7883      * field's data value (defaults to the underlying DOM element's name)
7884      */
7885     hiddenName: undefined,
7886     /**
7887      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7888      */
7889     listClass: '',
7890     /**
7891      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7892      */
7893     selectedClass: 'active',
7894     
7895     /**
7896      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7897      */
7898     shadow:'sides',
7899     /**
7900      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7901      * anchor positions (defaults to 'tl-bl')
7902      */
7903     listAlign: 'tl-bl?',
7904     /**
7905      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7906      */
7907     maxHeight: 300,
7908     /**
7909      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7910      * query specified by the allQuery config option (defaults to 'query')
7911      */
7912     triggerAction: 'query',
7913     /**
7914      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7915      * (defaults to 4, does not apply if editable = false)
7916      */
7917     minChars : 4,
7918     /**
7919      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7920      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7921      */
7922     typeAhead: false,
7923     /**
7924      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7925      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7926      */
7927     queryDelay: 500,
7928     /**
7929      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7930      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7931      */
7932     pageSize: 0,
7933     /**
7934      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7935      * when editable = true (defaults to false)
7936      */
7937     selectOnFocus:false,
7938     /**
7939      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7940      */
7941     queryParam: 'query',
7942     /**
7943      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7944      * when mode = 'remote' (defaults to 'Loading...')
7945      */
7946     loadingText: 'Loading...',
7947     /**
7948      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7949      */
7950     resizable: false,
7951     /**
7952      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7953      */
7954     handleHeight : 8,
7955     /**
7956      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7957      * traditional select (defaults to true)
7958      */
7959     editable: true,
7960     /**
7961      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7962      */
7963     allQuery: '',
7964     /**
7965      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7966      */
7967     mode: 'remote',
7968     /**
7969      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7970      * listWidth has a higher value)
7971      */
7972     minListWidth : 70,
7973     /**
7974      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7975      * allow the user to set arbitrary text into the field (defaults to false)
7976      */
7977     forceSelection:false,
7978     /**
7979      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7980      * if typeAhead = true (defaults to 250)
7981      */
7982     typeAheadDelay : 250,
7983     /**
7984      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7985      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7986      */
7987     valueNotFoundText : undefined,
7988     /**
7989      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7990      */
7991     blockFocus : false,
7992     
7993     /**
7994      * @cfg {Boolean} disableClear Disable showing of clear button.
7995      */
7996     disableClear : false,
7997     /**
7998      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7999      */
8000     alwaysQuery : false,
8001     
8002     /**
8003      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8004      */
8005     multiple : false,
8006     
8007     //private
8008     addicon : false,
8009     editicon: false,
8010     
8011     page: 0,
8012     hasQuery: false,
8013     append: false,
8014     loadNext: false,
8015     item: [],
8016     
8017     // element that contains real text value.. (when hidden is used..)
8018      
8019     // private
8020     initEvents: function(){
8021         
8022         if (!this.store) {
8023             throw "can not find store for combo";
8024         }
8025         this.store = Roo.factory(this.store, Roo.data);
8026         
8027         
8028         
8029         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8030         
8031         
8032         if(this.hiddenName){
8033             
8034             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8035             
8036             this.hiddenField.dom.value =
8037                 this.hiddenValue !== undefined ? this.hiddenValue :
8038                 this.value !== undefined ? this.value : '';
8039
8040             // prevent input submission
8041             this.el.dom.removeAttribute('name');
8042             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8043              
8044              
8045         }
8046         //if(Roo.isGecko){
8047         //    this.el.dom.setAttribute('autocomplete', 'off');
8048         //}
8049
8050         var cls = 'x-combo-list';
8051         this.list = this.el.select('ul.dropdown-menu',true).first();
8052
8053         //this.list = new Roo.Layer({
8054         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8055         //});
8056         
8057         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8058         this.list.setWidth(lw);
8059         
8060         this.list.on('mouseover', this.onViewOver, this);
8061         this.list.on('mousemove', this.onViewMove, this);
8062         
8063         this.list.on('scroll', this.onViewScroll, this);
8064         
8065         /*
8066         this.list.swallowEvent('mousewheel');
8067         this.assetHeight = 0;
8068
8069         if(this.title){
8070             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8071             this.assetHeight += this.header.getHeight();
8072         }
8073
8074         this.innerList = this.list.createChild({cls:cls+'-inner'});
8075         this.innerList.on('mouseover', this.onViewOver, this);
8076         this.innerList.on('mousemove', this.onViewMove, this);
8077         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8078         
8079         if(this.allowBlank && !this.pageSize && !this.disableClear){
8080             this.footer = this.list.createChild({cls:cls+'-ft'});
8081             this.pageTb = new Roo.Toolbar(this.footer);
8082            
8083         }
8084         if(this.pageSize){
8085             this.footer = this.list.createChild({cls:cls+'-ft'});
8086             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8087                     {pageSize: this.pageSize});
8088             
8089         }
8090         
8091         if (this.pageTb && this.allowBlank && !this.disableClear) {
8092             var _this = this;
8093             this.pageTb.add(new Roo.Toolbar.Fill(), {
8094                 cls: 'x-btn-icon x-btn-clear',
8095                 text: '&#160;',
8096                 handler: function()
8097                 {
8098                     _this.collapse();
8099                     _this.clearValue();
8100                     _this.onSelect(false, -1);
8101                 }
8102             });
8103         }
8104         if (this.footer) {
8105             this.assetHeight += this.footer.getHeight();
8106         }
8107         */
8108             
8109         if(!this.tpl){
8110             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8111         }
8112
8113         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8114             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8115         });
8116         //this.view.wrapEl.setDisplayed(false);
8117         this.view.on('click', this.onViewClick, this);
8118         
8119         
8120         
8121         this.store.on('beforeload', this.onBeforeLoad, this);
8122         this.store.on('load', this.onLoad, this);
8123         this.store.on('loadexception', this.onLoadException, this);
8124         /*
8125         if(this.resizable){
8126             this.resizer = new Roo.Resizable(this.list,  {
8127                pinned:true, handles:'se'
8128             });
8129             this.resizer.on('resize', function(r, w, h){
8130                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8131                 this.listWidth = w;
8132                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8133                 this.restrictHeight();
8134             }, this);
8135             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8136         }
8137         */
8138         if(!this.editable){
8139             this.editable = true;
8140             this.setEditable(false);
8141         }
8142         
8143         /*
8144         
8145         if (typeof(this.events.add.listeners) != 'undefined') {
8146             
8147             this.addicon = this.wrap.createChild(
8148                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8149        
8150             this.addicon.on('click', function(e) {
8151                 this.fireEvent('add', this);
8152             }, this);
8153         }
8154         if (typeof(this.events.edit.listeners) != 'undefined') {
8155             
8156             this.editicon = this.wrap.createChild(
8157                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8158             if (this.addicon) {
8159                 this.editicon.setStyle('margin-left', '40px');
8160             }
8161             this.editicon.on('click', function(e) {
8162                 
8163                 // we fire even  if inothing is selected..
8164                 this.fireEvent('edit', this, this.lastData );
8165                 
8166             }, this);
8167         }
8168         */
8169         
8170         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8171             "up" : function(e){
8172                 this.inKeyMode = true;
8173                 this.selectPrev();
8174             },
8175
8176             "down" : function(e){
8177                 if(!this.isExpanded()){
8178                     this.onTriggerClick();
8179                 }else{
8180                     this.inKeyMode = true;
8181                     this.selectNext();
8182                 }
8183             },
8184
8185             "enter" : function(e){
8186                 this.onViewClick();
8187                 //return true;
8188             },
8189
8190             "esc" : function(e){
8191                 this.collapse();
8192             },
8193
8194             "tab" : function(e){
8195                 this.collapse();
8196                 
8197                 if(this.fireEvent("specialkey", this, e)){
8198                     this.onViewClick(false);
8199                 }
8200                 
8201                 return true;
8202             },
8203
8204             scope : this,
8205
8206             doRelay : function(foo, bar, hname){
8207                 if(hname == 'down' || this.scope.isExpanded()){
8208                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8209                 }
8210                 return true;
8211             },
8212
8213             forceKeyDown: true
8214         });
8215         
8216         
8217         this.queryDelay = Math.max(this.queryDelay || 10,
8218                 this.mode == 'local' ? 10 : 250);
8219         
8220         
8221         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8222         
8223         if(this.typeAhead){
8224             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8225         }
8226         if(this.editable !== false){
8227             this.inputEl().on("keyup", this.onKeyUp, this);
8228         }
8229         if(this.forceSelection){
8230             this.on('blur', this.doForce, this);
8231         }
8232         
8233         if(this.multiple){
8234             this.choices = this.el.select('ul.select2-choices', true).first();
8235             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8236         }
8237     },
8238
8239     onDestroy : function(){
8240         if(this.view){
8241             this.view.setStore(null);
8242             this.view.el.removeAllListeners();
8243             this.view.el.remove();
8244             this.view.purgeListeners();
8245         }
8246         if(this.list){
8247             this.list.dom.innerHTML  = '';
8248         }
8249         if(this.store){
8250             this.store.un('beforeload', this.onBeforeLoad, this);
8251             this.store.un('load', this.onLoad, this);
8252             this.store.un('loadexception', this.onLoadException, this);
8253         }
8254         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8255     },
8256
8257     // private
8258     fireKey : function(e){
8259         if(e.isNavKeyPress() && !this.list.isVisible()){
8260             this.fireEvent("specialkey", this, e);
8261         }
8262     },
8263
8264     // private
8265     onResize: function(w, h){
8266 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8267 //        
8268 //        if(typeof w != 'number'){
8269 //            // we do not handle it!?!?
8270 //            return;
8271 //        }
8272 //        var tw = this.trigger.getWidth();
8273 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8274 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8275 //        var x = w - tw;
8276 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8277 //            
8278 //        //this.trigger.setStyle('left', x+'px');
8279 //        
8280 //        if(this.list && this.listWidth === undefined){
8281 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8282 //            this.list.setWidth(lw);
8283 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8284 //        }
8285         
8286     
8287         
8288     },
8289
8290     /**
8291      * Allow or prevent the user from directly editing the field text.  If false is passed,
8292      * the user will only be able to select from the items defined in the dropdown list.  This method
8293      * is the runtime equivalent of setting the 'editable' config option at config time.
8294      * @param {Boolean} value True to allow the user to directly edit the field text
8295      */
8296     setEditable : function(value){
8297         if(value == this.editable){
8298             return;
8299         }
8300         this.editable = value;
8301         if(!value){
8302             this.inputEl().dom.setAttribute('readOnly', true);
8303             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8304             this.inputEl().addClass('x-combo-noedit');
8305         }else{
8306             this.inputEl().dom.setAttribute('readOnly', false);
8307             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8308             this.inputEl().removeClass('x-combo-noedit');
8309         }
8310     },
8311
8312     // private
8313     
8314     onBeforeLoad : function(combo,opts){
8315         if(!this.hasFocus){
8316             return;
8317         }
8318          if (!opts.add) {
8319             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8320          }
8321         this.restrictHeight();
8322         this.selectedIndex = -1;
8323     },
8324
8325     // private
8326     onLoad : function(){
8327         
8328         this.hasQuery = false;
8329         
8330         if(!this.hasFocus){
8331             return;
8332         }
8333         
8334         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8335             this.loading.hide();
8336         }
8337         
8338         if(this.store.getCount() > 0){
8339             this.expand();
8340             this.restrictHeight();
8341             if(this.lastQuery == this.allQuery){
8342                 if(this.editable){
8343                     this.inputEl().dom.select();
8344                 }
8345                 if(!this.selectByValue(this.value, true)){
8346                     this.select(0, true);
8347                 }
8348             }else{
8349                 this.selectNext();
8350                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8351                     this.taTask.delay(this.typeAheadDelay);
8352                 }
8353             }
8354         }else{
8355             this.onEmptyResults();
8356         }
8357         
8358         //this.el.focus();
8359     },
8360     // private
8361     onLoadException : function()
8362     {
8363         this.hasQuery = false;
8364         
8365         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8366             this.loading.hide();
8367         }
8368         
8369         this.collapse();
8370         Roo.log(this.store.reader.jsonData);
8371         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8372             // fixme
8373             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8374         }
8375         
8376         
8377     },
8378     // private
8379     onTypeAhead : function(){
8380         if(this.store.getCount() > 0){
8381             var r = this.store.getAt(0);
8382             var newValue = r.data[this.displayField];
8383             var len = newValue.length;
8384             var selStart = this.getRawValue().length;
8385             
8386             if(selStart != len){
8387                 this.setRawValue(newValue);
8388                 this.selectText(selStart, newValue.length);
8389             }
8390         }
8391     },
8392
8393     // private
8394     onSelect : function(record, index){
8395         
8396         if(this.fireEvent('beforeselect', this, record, index) !== false){
8397         
8398             this.setFromData(index > -1 ? record.data : false);
8399             
8400             this.collapse();
8401             this.fireEvent('select', this, record, index);
8402         }
8403     },
8404
8405     /**
8406      * Returns the currently selected field value or empty string if no value is set.
8407      * @return {String} value The selected value
8408      */
8409     getValue : function(){
8410         
8411         if(this.multiple){
8412             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8413         }
8414         
8415         if(this.valueField){
8416             return typeof this.value != 'undefined' ? this.value : '';
8417         }else{
8418             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8419         }
8420     },
8421
8422     /**
8423      * Clears any text/value currently set in the field
8424      */
8425     clearValue : function(){
8426         if(this.hiddenField){
8427             this.hiddenField.dom.value = '';
8428         }
8429         this.value = '';
8430         this.setRawValue('');
8431         this.lastSelectionText = '';
8432         
8433     },
8434
8435     /**
8436      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8437      * will be displayed in the field.  If the value does not match the data value of an existing item,
8438      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8439      * Otherwise the field will be blank (although the value will still be set).
8440      * @param {String} value The value to match
8441      */
8442     setValue : function(v){
8443         if(this.multiple){
8444             this.syncValue();
8445             return;
8446         }
8447         
8448         var text = v;
8449         if(this.valueField){
8450             var r = this.findRecord(this.valueField, v);
8451             if(r){
8452                 text = r.data[this.displayField];
8453             }else if(this.valueNotFoundText !== undefined){
8454                 text = this.valueNotFoundText;
8455             }
8456         }
8457         this.lastSelectionText = text;
8458         if(this.hiddenField){
8459             this.hiddenField.dom.value = v;
8460         }
8461         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8462         this.value = v;
8463     },
8464     /**
8465      * @property {Object} the last set data for the element
8466      */
8467     
8468     lastData : false,
8469     /**
8470      * Sets the value of the field based on a object which is related to the record format for the store.
8471      * @param {Object} value the value to set as. or false on reset?
8472      */
8473     setFromData : function(o){
8474         
8475         if(this.multiple){
8476             this.addItem(o);
8477             return;
8478         }
8479             
8480         var dv = ''; // display value
8481         var vv = ''; // value value..
8482         this.lastData = o;
8483         if (this.displayField) {
8484             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8485         } else {
8486             // this is an error condition!!!
8487             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8488         }
8489         
8490         if(this.valueField){
8491             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8492         }
8493         
8494         if(this.hiddenField){
8495             this.hiddenField.dom.value = vv;
8496             
8497             this.lastSelectionText = dv;
8498             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8499             this.value = vv;
8500             return;
8501         }
8502         // no hidden field.. - we store the value in 'value', but still display
8503         // display field!!!!
8504         this.lastSelectionText = dv;
8505         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8506         this.value = vv;
8507         
8508         
8509     },
8510     // private
8511     reset : function(){
8512         // overridden so that last data is reset..
8513         this.setValue(this.originalValue);
8514         this.clearInvalid();
8515         this.lastData = false;
8516         if (this.view) {
8517             this.view.clearSelections();
8518         }
8519     },
8520     // private
8521     findRecord : function(prop, value){
8522         var record;
8523         if(this.store.getCount() > 0){
8524             this.store.each(function(r){
8525                 if(r.data[prop] == value){
8526                     record = r;
8527                     return false;
8528                 }
8529                 return true;
8530             });
8531         }
8532         return record;
8533     },
8534     
8535     getName: function()
8536     {
8537         // returns hidden if it's set..
8538         if (!this.rendered) {return ''};
8539         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8540         
8541     },
8542     // private
8543     onViewMove : function(e, t){
8544         this.inKeyMode = false;
8545     },
8546
8547     // private
8548     onViewOver : function(e, t){
8549         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8550             return;
8551         }
8552         var item = this.view.findItemFromChild(t);
8553         if(item){
8554             var index = this.view.indexOf(item);
8555             this.select(index, false);
8556         }
8557     },
8558
8559     // private
8560     onViewClick : function(doFocus)
8561     {
8562         var index = this.view.getSelectedIndexes()[0];
8563         var r = this.store.getAt(index);
8564         if(r){
8565             this.onSelect(r, index);
8566         }
8567         if(doFocus !== false && !this.blockFocus){
8568             this.inputEl().focus();
8569         }
8570     },
8571
8572     // private
8573     restrictHeight : function(){
8574         //this.innerList.dom.style.height = '';
8575         //var inner = this.innerList.dom;
8576         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8577         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8578         //this.list.beginUpdate();
8579         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8580         this.list.alignTo(this.inputEl(), this.listAlign);
8581         //this.list.endUpdate();
8582     },
8583
8584     // private
8585     onEmptyResults : function(){
8586         this.collapse();
8587     },
8588
8589     /**
8590      * Returns true if the dropdown list is expanded, else false.
8591      */
8592     isExpanded : function(){
8593         return this.list.isVisible();
8594     },
8595
8596     /**
8597      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8598      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8599      * @param {String} value The data value of the item to select
8600      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8601      * selected item if it is not currently in view (defaults to true)
8602      * @return {Boolean} True if the value matched an item in the list, else false
8603      */
8604     selectByValue : function(v, scrollIntoView){
8605         if(v !== undefined && v !== null){
8606             var r = this.findRecord(this.valueField || this.displayField, v);
8607             if(r){
8608                 this.select(this.store.indexOf(r), scrollIntoView);
8609                 return true;
8610             }
8611         }
8612         return false;
8613     },
8614
8615     /**
8616      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8617      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8618      * @param {Number} index The zero-based index of the list item to select
8619      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8620      * selected item if it is not currently in view (defaults to true)
8621      */
8622     select : function(index, scrollIntoView){
8623         this.selectedIndex = index;
8624         this.view.select(index);
8625         if(scrollIntoView !== false){
8626             var el = this.view.getNode(index);
8627             if(el){
8628                 //this.innerList.scrollChildIntoView(el, false);
8629                 
8630             }
8631         }
8632     },
8633
8634     // private
8635     selectNext : function(){
8636         var ct = this.store.getCount();
8637         if(ct > 0){
8638             if(this.selectedIndex == -1){
8639                 this.select(0);
8640             }else if(this.selectedIndex < ct-1){
8641                 this.select(this.selectedIndex+1);
8642             }
8643         }
8644     },
8645
8646     // private
8647     selectPrev : function(){
8648         var ct = this.store.getCount();
8649         if(ct > 0){
8650             if(this.selectedIndex == -1){
8651                 this.select(0);
8652             }else if(this.selectedIndex != 0){
8653                 this.select(this.selectedIndex-1);
8654             }
8655         }
8656     },
8657
8658     // private
8659     onKeyUp : function(e){
8660         if(this.editable !== false && !e.isSpecialKey()){
8661             this.lastKey = e.getKey();
8662             this.dqTask.delay(this.queryDelay);
8663         }
8664     },
8665
8666     // private
8667     validateBlur : function(){
8668         return !this.list || !this.list.isVisible();   
8669     },
8670
8671     // private
8672     initQuery : function(){
8673         this.doQuery(this.getRawValue());
8674     },
8675
8676     // private
8677     doForce : function(){
8678         if(this.el.dom.value.length > 0){
8679             this.el.dom.value =
8680                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8681              
8682         }
8683     },
8684
8685     /**
8686      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8687      * query allowing the query action to be canceled if needed.
8688      * @param {String} query The SQL query to execute
8689      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8690      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8691      * saved in the current store (defaults to false)
8692      */
8693     doQuery : function(q, forceAll){
8694         
8695         if(q === undefined || q === null){
8696             q = '';
8697         }
8698         var qe = {
8699             query: q,
8700             forceAll: forceAll,
8701             combo: this,
8702             cancel:false
8703         };
8704         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8705             return false;
8706         }
8707         q = qe.query;
8708         
8709         forceAll = qe.forceAll;
8710         if(forceAll === true || (q.length >= this.minChars)){
8711             
8712             this.hasQuery = true;
8713             
8714             if(this.lastQuery != q || this.alwaysQuery){
8715                 this.lastQuery = q;
8716                 if(this.mode == 'local'){
8717                     this.selectedIndex = -1;
8718                     if(forceAll){
8719                         this.store.clearFilter();
8720                     }else{
8721                         this.store.filter(this.displayField, q);
8722                     }
8723                     this.onLoad();
8724                 }else{
8725                     this.store.baseParams[this.queryParam] = q;
8726                     
8727                     var options = {params : this.getParams(q)};
8728                     
8729                     if(this.loadNext){
8730                         options.add = true;
8731                         options.params.start = this.page * this.pageSize;
8732                     }
8733                     
8734                     this.store.load(options);
8735                     this.expand();
8736                 }
8737             }else{
8738                 this.selectedIndex = -1;
8739                 this.onLoad();   
8740             }
8741         }
8742         
8743         this.loadNext = false;
8744     },
8745
8746     // private
8747     getParams : function(q){
8748         var p = {};
8749         //p[this.queryParam] = q;
8750         
8751         if(this.pageSize){
8752             p.start = 0;
8753             p.limit = this.pageSize;
8754         }
8755         return p;
8756     },
8757
8758     /**
8759      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8760      */
8761     collapse : function(){
8762         if(!this.isExpanded()){
8763             return;
8764         }
8765         
8766         this.list.hide();
8767         Roo.get(document).un('mousedown', this.collapseIf, this);
8768         Roo.get(document).un('mousewheel', this.collapseIf, this);
8769         if (!this.editable) {
8770             Roo.get(document).un('keydown', this.listKeyPress, this);
8771         }
8772         this.fireEvent('collapse', this);
8773     },
8774
8775     // private
8776     collapseIf : function(e){
8777         var in_combo  = e.within(this.el);
8778         var in_list =  e.within(this.list);
8779         
8780         if (in_combo || in_list) {
8781             //e.stopPropagation();
8782             return;
8783         }
8784
8785         this.collapse();
8786         
8787     },
8788
8789     /**
8790      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8791      */
8792     expand : function(){
8793        
8794         if(this.isExpanded() || !this.hasFocus){
8795             return;
8796         }
8797          Roo.log('expand');
8798         this.list.alignTo(this.inputEl(), this.listAlign);
8799         this.list.show();
8800         Roo.get(document).on('mousedown', this.collapseIf, this);
8801         Roo.get(document).on('mousewheel', this.collapseIf, this);
8802         if (!this.editable) {
8803             Roo.get(document).on('keydown', this.listKeyPress, this);
8804         }
8805         
8806         this.fireEvent('expand', this);
8807     },
8808
8809     // private
8810     // Implements the default empty TriggerField.onTriggerClick function
8811     onTriggerClick : function()
8812     {
8813         Roo.log('trigger click');
8814         
8815         if(this.disabled){
8816             return;
8817         }
8818         
8819         this.page = 0;
8820         this.loadNext = false;
8821         
8822         if(this.isExpanded()){
8823             this.collapse();
8824             if (!this.blockFocus) {
8825                 this.inputEl().focus();
8826             }
8827             
8828         }else {
8829             this.hasFocus = true;
8830             if(this.triggerAction == 'all') {
8831                 this.doQuery(this.allQuery, true);
8832             } else {
8833                 this.doQuery(this.getRawValue());
8834             }
8835             if (!this.blockFocus) {
8836                 this.inputEl().focus();
8837             }
8838         }
8839     },
8840     listKeyPress : function(e)
8841     {
8842         //Roo.log('listkeypress');
8843         // scroll to first matching element based on key pres..
8844         if (e.isSpecialKey()) {
8845             return false;
8846         }
8847         var k = String.fromCharCode(e.getKey()).toUpperCase();
8848         //Roo.log(k);
8849         var match  = false;
8850         var csel = this.view.getSelectedNodes();
8851         var cselitem = false;
8852         if (csel.length) {
8853             var ix = this.view.indexOf(csel[0]);
8854             cselitem  = this.store.getAt(ix);
8855             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8856                 cselitem = false;
8857             }
8858             
8859         }
8860         
8861         this.store.each(function(v) { 
8862             if (cselitem) {
8863                 // start at existing selection.
8864                 if (cselitem.id == v.id) {
8865                     cselitem = false;
8866                 }
8867                 return true;
8868             }
8869                 
8870             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8871                 match = this.store.indexOf(v);
8872                 return false;
8873             }
8874             return true;
8875         }, this);
8876         
8877         if (match === false) {
8878             return true; // no more action?
8879         }
8880         // scroll to?
8881         this.view.select(match);
8882         var sn = Roo.get(this.view.getSelectedNodes()[0])
8883         //sn.scrollIntoView(sn.dom.parentNode, false);
8884     },
8885     
8886     onViewScroll : function(e, t){
8887         
8888         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8889             return;
8890         }
8891         
8892         this.hasQuery = true;
8893         
8894         this.loading = this.list.select('.loading', true).first();
8895         
8896         if(this.loading === null){
8897             this.list.createChild({
8898                 tag: 'div',
8899                 cls: 'loading select2-more-results select2-active',
8900                 html: 'Loading more results...'
8901             })
8902             
8903             this.loading = this.list.select('.loading', true).first();
8904             
8905             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8906             
8907             this.loading.hide();
8908         }
8909         
8910         this.loading.show();
8911         
8912         var _combo = this;
8913         
8914         this.page++;
8915         this.loadNext = true;
8916         
8917         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8918         
8919         return;
8920     },
8921     
8922     addItem : function(o)
8923     {   
8924         var dv = ''; // display value
8925         
8926         if (this.displayField) {
8927             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8928         } else {
8929             // this is an error condition!!!
8930             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8931         }
8932         
8933         if(!dv.length){
8934             return;
8935         }
8936         
8937         var choice = this.choices.createChild({
8938             tag: 'li',
8939             cls: 'select2-search-choice',
8940             cn: [
8941                 {
8942                     tag: 'div',
8943                     html: dv
8944                 },
8945                 {
8946                     tag: 'a',
8947                     href: '#',
8948                     cls: 'select2-search-choice-close',
8949                     tabindex: '-1'
8950                 }
8951             ]
8952             
8953         }, this.searchField);
8954         
8955         var close = choice.select('a.select2-search-choice-close', true).first()
8956         
8957         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8958         
8959         this.item.push(o);
8960         this.lastData = o;
8961         
8962         this.syncValue();
8963         
8964         this.inputEl().dom.value = '';
8965         
8966     },
8967     
8968     onRemoveItem : function(e, _self, o)
8969     {
8970         Roo.log('remove item');
8971         var index = this.item.indexOf(o.data) * 1;
8972         
8973         if( index < 0){
8974             Roo.log('not this item?!');
8975             return;
8976         }
8977         
8978         this.item.splice(index, 1);
8979         o.item.remove();
8980         
8981         this.syncValue();
8982         
8983         this.fireEvent('remove', this);
8984         
8985     },
8986     
8987     syncValue : function()
8988     {
8989         if(!this.item.length){
8990             this.clearValue();
8991             return;
8992         }
8993             
8994         var value = [];
8995         var _this = this;
8996         Roo.each(this.item, function(i){
8997             if(_this.valueField){
8998                 value.push(i[_this.valueField]);
8999                 return;
9000             }
9001
9002             value.push(i);
9003         });
9004
9005         this.value = value.join(',');
9006
9007         if(this.hiddenField){
9008             this.hiddenField.dom.value = this.value;
9009         }
9010     },
9011     
9012     clearItem : function()
9013     {
9014         if(!this.multiple){
9015             return;
9016         }
9017         
9018         this.item = [];
9019         
9020         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9021            c.remove();
9022         });
9023         
9024         this.syncValue();
9025     }
9026     
9027     
9028
9029     /** 
9030     * @cfg {Boolean} grow 
9031     * @hide 
9032     */
9033     /** 
9034     * @cfg {Number} growMin 
9035     * @hide 
9036     */
9037     /** 
9038     * @cfg {Number} growMax 
9039     * @hide 
9040     */
9041     /**
9042      * @hide
9043      * @method autoSize
9044      */
9045 });
9046 /*
9047  * Based on:
9048  * Ext JS Library 1.1.1
9049  * Copyright(c) 2006-2007, Ext JS, LLC.
9050  *
9051  * Originally Released Under LGPL - original licence link has changed is not relivant.
9052  *
9053  * Fork - LGPL
9054  * <script type="text/javascript">
9055  */
9056
9057 /**
9058  * @class Roo.View
9059  * @extends Roo.util.Observable
9060  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9061  * This class also supports single and multi selection modes. <br>
9062  * Create a data model bound view:
9063  <pre><code>
9064  var store = new Roo.data.Store(...);
9065
9066  var view = new Roo.View({
9067     el : "my-element",
9068     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9069  
9070     singleSelect: true,
9071     selectedClass: "ydataview-selected",
9072     store: store
9073  });
9074
9075  // listen for node click?
9076  view.on("click", function(vw, index, node, e){
9077  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9078  });
9079
9080  // load XML data
9081  dataModel.load("foobar.xml");
9082  </code></pre>
9083  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9084  * <br><br>
9085  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9086  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9087  * 
9088  * Note: old style constructor is still suported (container, template, config)
9089  * 
9090  * @constructor
9091  * Create a new View
9092  * @param {Object} config The config object
9093  * 
9094  */
9095 Roo.View = function(config, depreciated_tpl, depreciated_config){
9096     
9097     if (typeof(depreciated_tpl) == 'undefined') {
9098         // new way.. - universal constructor.
9099         Roo.apply(this, config);
9100         this.el  = Roo.get(this.el);
9101     } else {
9102         // old format..
9103         this.el  = Roo.get(config);
9104         this.tpl = depreciated_tpl;
9105         Roo.apply(this, depreciated_config);
9106     }
9107     this.wrapEl  = this.el.wrap().wrap();
9108     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9109     
9110     
9111     if(typeof(this.tpl) == "string"){
9112         this.tpl = new Roo.Template(this.tpl);
9113     } else {
9114         // support xtype ctors..
9115         this.tpl = new Roo.factory(this.tpl, Roo);
9116     }
9117     
9118     
9119     this.tpl.compile();
9120    
9121   
9122     
9123      
9124     /** @private */
9125     this.addEvents({
9126         /**
9127          * @event beforeclick
9128          * Fires before a click is processed. Returns false to cancel the default action.
9129          * @param {Roo.View} this
9130          * @param {Number} index The index of the target node
9131          * @param {HTMLElement} node The target node
9132          * @param {Roo.EventObject} e The raw event object
9133          */
9134             "beforeclick" : true,
9135         /**
9136          * @event click
9137          * Fires when a template node is clicked.
9138          * @param {Roo.View} this
9139          * @param {Number} index The index of the target node
9140          * @param {HTMLElement} node The target node
9141          * @param {Roo.EventObject} e The raw event object
9142          */
9143             "click" : true,
9144         /**
9145          * @event dblclick
9146          * Fires when a template node is double clicked.
9147          * @param {Roo.View} this
9148          * @param {Number} index The index of the target node
9149          * @param {HTMLElement} node The target node
9150          * @param {Roo.EventObject} e The raw event object
9151          */
9152             "dblclick" : true,
9153         /**
9154          * @event contextmenu
9155          * Fires when a template node is right clicked.
9156          * @param {Roo.View} this
9157          * @param {Number} index The index of the target node
9158          * @param {HTMLElement} node The target node
9159          * @param {Roo.EventObject} e The raw event object
9160          */
9161             "contextmenu" : true,
9162         /**
9163          * @event selectionchange
9164          * Fires when the selected nodes change.
9165          * @param {Roo.View} this
9166          * @param {Array} selections Array of the selected nodes
9167          */
9168             "selectionchange" : true,
9169     
9170         /**
9171          * @event beforeselect
9172          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9173          * @param {Roo.View} this
9174          * @param {HTMLElement} node The node to be selected
9175          * @param {Array} selections Array of currently selected nodes
9176          */
9177             "beforeselect" : true,
9178         /**
9179          * @event preparedata
9180          * Fires on every row to render, to allow you to change the data.
9181          * @param {Roo.View} this
9182          * @param {Object} data to be rendered (change this)
9183          */
9184           "preparedata" : true
9185           
9186           
9187         });
9188
9189
9190
9191     this.el.on({
9192         "click": this.onClick,
9193         "dblclick": this.onDblClick,
9194         "contextmenu": this.onContextMenu,
9195         scope:this
9196     });
9197
9198     this.selections = [];
9199     this.nodes = [];
9200     this.cmp = new Roo.CompositeElementLite([]);
9201     if(this.store){
9202         this.store = Roo.factory(this.store, Roo.data);
9203         this.setStore(this.store, true);
9204     }
9205     
9206     if ( this.footer && this.footer.xtype) {
9207            
9208          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9209         
9210         this.footer.dataSource = this.store
9211         this.footer.container = fctr;
9212         this.footer = Roo.factory(this.footer, Roo);
9213         fctr.insertFirst(this.el);
9214         
9215         // this is a bit insane - as the paging toolbar seems to detach the el..
9216 //        dom.parentNode.parentNode.parentNode
9217          // they get detached?
9218     }
9219     
9220     
9221     Roo.View.superclass.constructor.call(this);
9222     
9223     
9224 };
9225
9226 Roo.extend(Roo.View, Roo.util.Observable, {
9227     
9228      /**
9229      * @cfg {Roo.data.Store} store Data store to load data from.
9230      */
9231     store : false,
9232     
9233     /**
9234      * @cfg {String|Roo.Element} el The container element.
9235      */
9236     el : '',
9237     
9238     /**
9239      * @cfg {String|Roo.Template} tpl The template used by this View 
9240      */
9241     tpl : false,
9242     /**
9243      * @cfg {String} dataName the named area of the template to use as the data area
9244      *                          Works with domtemplates roo-name="name"
9245      */
9246     dataName: false,
9247     /**
9248      * @cfg {String} selectedClass The css class to add to selected nodes
9249      */
9250     selectedClass : "x-view-selected",
9251      /**
9252      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9253      */
9254     emptyText : "",
9255     
9256     /**
9257      * @cfg {String} text to display on mask (default Loading)
9258      */
9259     mask : false,
9260     /**
9261      * @cfg {Boolean} multiSelect Allow multiple selection
9262      */
9263     multiSelect : false,
9264     /**
9265      * @cfg {Boolean} singleSelect Allow single selection
9266      */
9267     singleSelect:  false,
9268     
9269     /**
9270      * @cfg {Boolean} toggleSelect - selecting 
9271      */
9272     toggleSelect : false,
9273     
9274     /**
9275      * Returns the element this view is bound to.
9276      * @return {Roo.Element}
9277      */
9278     getEl : function(){
9279         return this.wrapEl;
9280     },
9281     
9282     
9283
9284     /**
9285      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9286      */
9287     refresh : function(){
9288         Roo.log('refresh');
9289         var t = this.tpl;
9290         
9291         // if we are using something like 'domtemplate', then
9292         // the what gets used is:
9293         // t.applySubtemplate(NAME, data, wrapping data..)
9294         // the outer template then get' applied with
9295         //     the store 'extra data'
9296         // and the body get's added to the
9297         //      roo-name="data" node?
9298         //      <span class='roo-tpl-{name}'></span> ?????
9299         
9300         
9301         
9302         this.clearSelections();
9303         this.el.update("");
9304         var html = [];
9305         var records = this.store.getRange();
9306         if(records.length < 1) {
9307             
9308             // is this valid??  = should it render a template??
9309             
9310             this.el.update(this.emptyText);
9311             return;
9312         }
9313         var el = this.el;
9314         if (this.dataName) {
9315             this.el.update(t.apply(this.store.meta)); //????
9316             el = this.el.child('.roo-tpl-' + this.dataName);
9317         }
9318         
9319         for(var i = 0, len = records.length; i < len; i++){
9320             var data = this.prepareData(records[i].data, i, records[i]);
9321             this.fireEvent("preparedata", this, data, i, records[i]);
9322             html[html.length] = Roo.util.Format.trim(
9323                 this.dataName ?
9324                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9325                     t.apply(data)
9326             );
9327         }
9328         
9329         
9330         
9331         el.update(html.join(""));
9332         this.nodes = el.dom.childNodes;
9333         this.updateIndexes(0);
9334     },
9335     
9336
9337     /**
9338      * Function to override to reformat the data that is sent to
9339      * the template for each node.
9340      * DEPRICATED - use the preparedata event handler.
9341      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9342      * a JSON object for an UpdateManager bound view).
9343      */
9344     prepareData : function(data, index, record)
9345     {
9346         this.fireEvent("preparedata", this, data, index, record);
9347         return data;
9348     },
9349
9350     onUpdate : function(ds, record){
9351          Roo.log('on update');   
9352         this.clearSelections();
9353         var index = this.store.indexOf(record);
9354         var n = this.nodes[index];
9355         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9356         n.parentNode.removeChild(n);
9357         this.updateIndexes(index, index);
9358     },
9359
9360     
9361     
9362 // --------- FIXME     
9363     onAdd : function(ds, records, index)
9364     {
9365         Roo.log(['on Add', ds, records, index] );        
9366         this.clearSelections();
9367         if(this.nodes.length == 0){
9368             this.refresh();
9369             return;
9370         }
9371         var n = this.nodes[index];
9372         for(var i = 0, len = records.length; i < len; i++){
9373             var d = this.prepareData(records[i].data, i, records[i]);
9374             if(n){
9375                 this.tpl.insertBefore(n, d);
9376             }else{
9377                 
9378                 this.tpl.append(this.el, d);
9379             }
9380         }
9381         this.updateIndexes(index);
9382     },
9383
9384     onRemove : function(ds, record, index){
9385         Roo.log('onRemove');
9386         this.clearSelections();
9387         var el = this.dataName  ?
9388             this.el.child('.roo-tpl-' + this.dataName) :
9389             this.el; 
9390         
9391         el.dom.removeChild(this.nodes[index]);
9392         this.updateIndexes(index);
9393     },
9394
9395     /**
9396      * Refresh an individual node.
9397      * @param {Number} index
9398      */
9399     refreshNode : function(index){
9400         this.onUpdate(this.store, this.store.getAt(index));
9401     },
9402
9403     updateIndexes : function(startIndex, endIndex){
9404         var ns = this.nodes;
9405         startIndex = startIndex || 0;
9406         endIndex = endIndex || ns.length - 1;
9407         for(var i = startIndex; i <= endIndex; i++){
9408             ns[i].nodeIndex = i;
9409         }
9410     },
9411
9412     /**
9413      * Changes the data store this view uses and refresh the view.
9414      * @param {Store} store
9415      */
9416     setStore : function(store, initial){
9417         if(!initial && this.store){
9418             this.store.un("datachanged", this.refresh);
9419             this.store.un("add", this.onAdd);
9420             this.store.un("remove", this.onRemove);
9421             this.store.un("update", this.onUpdate);
9422             this.store.un("clear", this.refresh);
9423             this.store.un("beforeload", this.onBeforeLoad);
9424             this.store.un("load", this.onLoad);
9425             this.store.un("loadexception", this.onLoad);
9426         }
9427         if(store){
9428           
9429             store.on("datachanged", this.refresh, this);
9430             store.on("add", this.onAdd, this);
9431             store.on("remove", this.onRemove, this);
9432             store.on("update", this.onUpdate, this);
9433             store.on("clear", this.refresh, this);
9434             store.on("beforeload", this.onBeforeLoad, this);
9435             store.on("load", this.onLoad, this);
9436             store.on("loadexception", this.onLoad, this);
9437         }
9438         
9439         if(store){
9440             this.refresh();
9441         }
9442     },
9443     /**
9444      * onbeforeLoad - masks the loading area.
9445      *
9446      */
9447     onBeforeLoad : function(store,opts)
9448     {
9449          Roo.log('onBeforeLoad');   
9450         if (!opts.add) {
9451             this.el.update("");
9452         }
9453         this.el.mask(this.mask ? this.mask : "Loading" ); 
9454     },
9455     onLoad : function ()
9456     {
9457         this.el.unmask();
9458     },
9459     
9460
9461     /**
9462      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9463      * @param {HTMLElement} node
9464      * @return {HTMLElement} The template node
9465      */
9466     findItemFromChild : function(node){
9467         var el = this.dataName  ?
9468             this.el.child('.roo-tpl-' + this.dataName,true) :
9469             this.el.dom; 
9470         
9471         if(!node || node.parentNode == el){
9472                     return node;
9473             }
9474             var p = node.parentNode;
9475             while(p && p != el){
9476             if(p.parentNode == el){
9477                 return p;
9478             }
9479             p = p.parentNode;
9480         }
9481             return null;
9482     },
9483
9484     /** @ignore */
9485     onClick : function(e){
9486         var item = this.findItemFromChild(e.getTarget());
9487         if(item){
9488             var index = this.indexOf(item);
9489             if(this.onItemClick(item, index, e) !== false){
9490                 this.fireEvent("click", this, index, item, e);
9491             }
9492         }else{
9493             this.clearSelections();
9494         }
9495     },
9496
9497     /** @ignore */
9498     onContextMenu : function(e){
9499         var item = this.findItemFromChild(e.getTarget());
9500         if(item){
9501             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9502         }
9503     },
9504
9505     /** @ignore */
9506     onDblClick : function(e){
9507         var item = this.findItemFromChild(e.getTarget());
9508         if(item){
9509             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9510         }
9511     },
9512
9513     onItemClick : function(item, index, e)
9514     {
9515         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9516             return false;
9517         }
9518         if (this.toggleSelect) {
9519             var m = this.isSelected(item) ? 'unselect' : 'select';
9520             Roo.log(m);
9521             var _t = this;
9522             _t[m](item, true, false);
9523             return true;
9524         }
9525         if(this.multiSelect || this.singleSelect){
9526             if(this.multiSelect && e.shiftKey && this.lastSelection){
9527                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9528             }else{
9529                 this.select(item, this.multiSelect && e.ctrlKey);
9530                 this.lastSelection = item;
9531             }
9532             e.preventDefault();
9533         }
9534         return true;
9535     },
9536
9537     /**
9538      * Get the number of selected nodes.
9539      * @return {Number}
9540      */
9541     getSelectionCount : function(){
9542         return this.selections.length;
9543     },
9544
9545     /**
9546      * Get the currently selected nodes.
9547      * @return {Array} An array of HTMLElements
9548      */
9549     getSelectedNodes : function(){
9550         return this.selections;
9551     },
9552
9553     /**
9554      * Get the indexes of the selected nodes.
9555      * @return {Array}
9556      */
9557     getSelectedIndexes : function(){
9558         var indexes = [], s = this.selections;
9559         for(var i = 0, len = s.length; i < len; i++){
9560             indexes.push(s[i].nodeIndex);
9561         }
9562         return indexes;
9563     },
9564
9565     /**
9566      * Clear all selections
9567      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9568      */
9569     clearSelections : function(suppressEvent){
9570         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9571             this.cmp.elements = this.selections;
9572             this.cmp.removeClass(this.selectedClass);
9573             this.selections = [];
9574             if(!suppressEvent){
9575                 this.fireEvent("selectionchange", this, this.selections);
9576             }
9577         }
9578     },
9579
9580     /**
9581      * Returns true if the passed node is selected
9582      * @param {HTMLElement/Number} node The node or node index
9583      * @return {Boolean}
9584      */
9585     isSelected : function(node){
9586         var s = this.selections;
9587         if(s.length < 1){
9588             return false;
9589         }
9590         node = this.getNode(node);
9591         return s.indexOf(node) !== -1;
9592     },
9593
9594     /**
9595      * Selects nodes.
9596      * @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
9597      * @param {Boolean} keepExisting (optional) true to keep existing selections
9598      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9599      */
9600     select : function(nodeInfo, keepExisting, suppressEvent){
9601         if(nodeInfo instanceof Array){
9602             if(!keepExisting){
9603                 this.clearSelections(true);
9604             }
9605             for(var i = 0, len = nodeInfo.length; i < len; i++){
9606                 this.select(nodeInfo[i], true, true);
9607             }
9608             return;
9609         } 
9610         var node = this.getNode(nodeInfo);
9611         if(!node || this.isSelected(node)){
9612             return; // already selected.
9613         }
9614         if(!keepExisting){
9615             this.clearSelections(true);
9616         }
9617         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9618             Roo.fly(node).addClass(this.selectedClass);
9619             this.selections.push(node);
9620             if(!suppressEvent){
9621                 this.fireEvent("selectionchange", this, this.selections);
9622             }
9623         }
9624         
9625         
9626     },
9627       /**
9628      * Unselects nodes.
9629      * @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
9630      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9631      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9632      */
9633     unselect : function(nodeInfo, keepExisting, suppressEvent)
9634     {
9635         if(nodeInfo instanceof Array){
9636             Roo.each(this.selections, function(s) {
9637                 this.unselect(s, nodeInfo);
9638             }, this);
9639             return;
9640         }
9641         var node = this.getNode(nodeInfo);
9642         if(!node || !this.isSelected(node)){
9643             Roo.log("not selected");
9644             return; // not selected.
9645         }
9646         // fireevent???
9647         var ns = [];
9648         Roo.each(this.selections, function(s) {
9649             if (s == node ) {
9650                 Roo.fly(node).removeClass(this.selectedClass);
9651
9652                 return;
9653             }
9654             ns.push(s);
9655         },this);
9656         
9657         this.selections= ns;
9658         this.fireEvent("selectionchange", this, this.selections);
9659     },
9660
9661     /**
9662      * Gets a template node.
9663      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9664      * @return {HTMLElement} The node or null if it wasn't found
9665      */
9666     getNode : function(nodeInfo){
9667         if(typeof nodeInfo == "string"){
9668             return document.getElementById(nodeInfo);
9669         }else if(typeof nodeInfo == "number"){
9670             return this.nodes[nodeInfo];
9671         }
9672         return nodeInfo;
9673     },
9674
9675     /**
9676      * Gets a range template nodes.
9677      * @param {Number} startIndex
9678      * @param {Number} endIndex
9679      * @return {Array} An array of nodes
9680      */
9681     getNodes : function(start, end){
9682         var ns = this.nodes;
9683         start = start || 0;
9684         end = typeof end == "undefined" ? ns.length - 1 : end;
9685         var nodes = [];
9686         if(start <= end){
9687             for(var i = start; i <= end; i++){
9688                 nodes.push(ns[i]);
9689             }
9690         } else{
9691             for(var i = start; i >= end; i--){
9692                 nodes.push(ns[i]);
9693             }
9694         }
9695         return nodes;
9696     },
9697
9698     /**
9699      * Finds the index of the passed node
9700      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9701      * @return {Number} The index of the node or -1
9702      */
9703     indexOf : function(node){
9704         node = this.getNode(node);
9705         if(typeof node.nodeIndex == "number"){
9706             return node.nodeIndex;
9707         }
9708         var ns = this.nodes;
9709         for(var i = 0, len = ns.length; i < len; i++){
9710             if(ns[i] == node){
9711                 return i;
9712             }
9713         }
9714         return -1;
9715     }
9716 });
9717 /*
9718  * - LGPL
9719  *
9720  * based on jquery fullcalendar
9721  * 
9722  */
9723
9724 Roo.bootstrap = Roo.bootstrap || {};
9725 /**
9726  * @class Roo.bootstrap.Calendar
9727  * @extends Roo.bootstrap.Component
9728  * Bootstrap Calendar class
9729  * @cfg {Boolean} loadMask (true|false) default false
9730     
9731  * @constructor
9732  * Create a new Container
9733  * @param {Object} config The config object
9734  */
9735
9736
9737
9738 Roo.bootstrap.Calendar = function(config){
9739     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9740      this.addEvents({
9741         /**
9742              * @event select
9743              * Fires when a date is selected
9744              * @param {DatePicker} this
9745              * @param {Date} date The selected date
9746              */
9747         'select': true,
9748         /**
9749              * @event monthchange
9750              * Fires when the displayed month changes 
9751              * @param {DatePicker} this
9752              * @param {Date} date The selected month
9753              */
9754         'monthchange': true,
9755         /**
9756              * @event evententer
9757              * Fires when mouse over an event
9758              * @param {Calendar} this
9759              * @param {event} Event
9760              */
9761         'evententer': true,
9762         /**
9763              * @event eventleave
9764              * Fires when the mouse leaves an
9765              * @param {Calendar} this
9766              * @param {event}
9767              */
9768         'eventleave': true,
9769         /**
9770              * @event eventclick
9771              * Fires when the mouse click an
9772              * @param {Calendar} this
9773              * @param {event}
9774              */
9775         'eventclick': true
9776         
9777     });
9778
9779 };
9780
9781 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9782     
9783      /**
9784      * @cfg {Number} startDay
9785      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9786      */
9787     startDay : 0,
9788     
9789     loadMask : false,
9790       
9791     getAutoCreate : function(){
9792         
9793         
9794         var fc_button = function(name, corner, style, content ) {
9795             return Roo.apply({},{
9796                 tag : 'span',
9797                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9798                          (corner.length ?
9799                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9800                             ''
9801                         ),
9802                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9803                 unselectable: 'on'
9804             });
9805         };
9806         
9807         var header = {
9808             tag : 'table',
9809             cls : 'fc-header',
9810             style : 'width:100%',
9811             cn : [
9812                 {
9813                     tag: 'tr',
9814                     cn : [
9815                         {
9816                             tag : 'td',
9817                             cls : 'fc-header-left',
9818                             cn : [
9819                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9820                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9821                                 { tag: 'span', cls: 'fc-header-space' },
9822                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9823                                 
9824                                 
9825                             ]
9826                         },
9827                         
9828                         {
9829                             tag : 'td',
9830                             cls : 'fc-header-center',
9831                             cn : [
9832                                 {
9833                                     tag: 'span',
9834                                     cls: 'fc-header-title',
9835                                     cn : {
9836                                         tag: 'H2',
9837                                         html : 'month / year'
9838                                     }
9839                                 }
9840                                 
9841                             ]
9842                         },
9843                         {
9844                             tag : 'td',
9845                             cls : 'fc-header-right',
9846                             cn : [
9847                           /*      fc_button('month', 'left', '', 'month' ),
9848                                 fc_button('week', '', '', 'week' ),
9849                                 fc_button('day', 'right', '', 'day' )
9850                             */    
9851                                 
9852                             ]
9853                         }
9854                         
9855                     ]
9856                 }
9857             ]
9858         };
9859         
9860        
9861         var cal_heads = function() {
9862             var ret = [];
9863             // fixme - handle this.
9864             
9865             for (var i =0; i < Date.dayNames.length; i++) {
9866                 var d = Date.dayNames[i];
9867                 ret.push({
9868                     tag: 'th',
9869                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9870                     html : d.substring(0,3)
9871                 });
9872                 
9873             }
9874             ret[0].cls += ' fc-first';
9875             ret[6].cls += ' fc-last';
9876             return ret;
9877         };
9878         var cal_cell = function(n) {
9879             return  {
9880                 tag: 'td',
9881                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9882                 cn : [
9883                     {
9884                         cn : [
9885                             {
9886                                 cls: 'fc-day-number',
9887                                 html: 'D'
9888                             },
9889                             {
9890                                 cls: 'fc-day-content',
9891                              
9892                                 cn : [
9893                                      {
9894                                         style: 'position: relative;' // height: 17px;
9895                                     }
9896                                 ]
9897                             }
9898                             
9899                             
9900                         ]
9901                     }
9902                 ]
9903                 
9904             }
9905         };
9906         var cal_rows = function() {
9907             
9908             var ret = []
9909             for (var r = 0; r < 6; r++) {
9910                 var row= {
9911                     tag : 'tr',
9912                     cls : 'fc-week',
9913                     cn : []
9914                 };
9915                 
9916                 for (var i =0; i < Date.dayNames.length; i++) {
9917                     var d = Date.dayNames[i];
9918                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9919
9920                 }
9921                 row.cn[0].cls+=' fc-first';
9922                 row.cn[0].cn[0].style = 'min-height:90px';
9923                 row.cn[6].cls+=' fc-last';
9924                 ret.push(row);
9925                 
9926             }
9927             ret[0].cls += ' fc-first';
9928             ret[4].cls += ' fc-prev-last';
9929             ret[5].cls += ' fc-last';
9930             return ret;
9931             
9932         };
9933         
9934         var cal_table = {
9935             tag: 'table',
9936             cls: 'fc-border-separate',
9937             style : 'width:100%',
9938             cellspacing  : 0,
9939             cn : [
9940                 { 
9941                     tag: 'thead',
9942                     cn : [
9943                         { 
9944                             tag: 'tr',
9945                             cls : 'fc-first fc-last',
9946                             cn : cal_heads()
9947                         }
9948                     ]
9949                 },
9950                 { 
9951                     tag: 'tbody',
9952                     cn : cal_rows()
9953                 }
9954                   
9955             ]
9956         };
9957          
9958          var cfg = {
9959             cls : 'fc fc-ltr',
9960             cn : [
9961                 header,
9962                 {
9963                     cls : 'fc-content',
9964                     style : "position: relative;",
9965                     cn : [
9966                         {
9967                             cls : 'fc-view fc-view-month fc-grid',
9968                             style : 'position: relative',
9969                             unselectable : 'on',
9970                             cn : [
9971                                 {
9972                                     cls : 'fc-event-container',
9973                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9974                                 },
9975                                 cal_table
9976                             ]
9977                         }
9978                     ]
9979     
9980                 }
9981            ] 
9982             
9983         };
9984         
9985          
9986         
9987         return cfg;
9988     },
9989     
9990     
9991     initEvents : function()
9992     {
9993         if(!this.store){
9994             throw "can not find store for calendar";
9995         }
9996         
9997         var mark = {
9998             tag: "div",
9999             cls:"x-dlg-mask",
10000             style: "text-align:center",
10001             cn: [
10002                 {
10003                     tag: "div",
10004                     style: "background-color:white;width:50%;margin:250 auto",
10005                     cn: [
10006                         {
10007                             tag: "img",
10008                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10009                         },
10010                         {
10011                             tag: "span",
10012                             html: "Loading"
10013                         }
10014                         
10015                     ]
10016                 }
10017             ]
10018         }
10019         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10020         
10021         var size = this.el.select('.fc-content', true).first().getSize();
10022         this.maskEl.setSize(size.width, size.height);
10023         this.maskEl.enableDisplayMode("block");
10024         if(!this.loadMask){
10025             this.maskEl.hide();
10026         }
10027         
10028         this.store = Roo.factory(this.store, Roo.data);
10029         this.store.on('load', this.onLoad, this);
10030         this.store.on('beforeload', this.onBeforeLoad, this);
10031         
10032         this.resize();
10033         
10034         this.cells = this.el.select('.fc-day',true);
10035         //Roo.log(this.cells);
10036         this.textNodes = this.el.query('.fc-day-number');
10037         this.cells.addClassOnOver('fc-state-hover');
10038         
10039         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10040         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10041         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10042         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10043         
10044         this.on('monthchange', this.onMonthChange, this);
10045         
10046         this.update(new Date().clearTime());
10047     },
10048     
10049     resize : function() {
10050         var sz  = this.el.getSize();
10051         
10052         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10053         this.el.select('.fc-day-content div',true).setHeight(34);
10054     },
10055     
10056     
10057     // private
10058     showPrevMonth : function(e){
10059         this.update(this.activeDate.add("mo", -1));
10060     },
10061     showToday : function(e){
10062         this.update(new Date().clearTime());
10063     },
10064     // private
10065     showNextMonth : function(e){
10066         this.update(this.activeDate.add("mo", 1));
10067     },
10068
10069     // private
10070     showPrevYear : function(){
10071         this.update(this.activeDate.add("y", -1));
10072     },
10073
10074     // private
10075     showNextYear : function(){
10076         this.update(this.activeDate.add("y", 1));
10077     },
10078
10079     
10080    // private
10081     update : function(date)
10082     {
10083         var vd = this.activeDate;
10084         this.activeDate = date;
10085 //        if(vd && this.el){
10086 //            var t = date.getTime();
10087 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10088 //                Roo.log('using add remove');
10089 //                
10090 //                this.fireEvent('monthchange', this, date);
10091 //                
10092 //                this.cells.removeClass("fc-state-highlight");
10093 //                this.cells.each(function(c){
10094 //                   if(c.dateValue == t){
10095 //                       c.addClass("fc-state-highlight");
10096 //                       setTimeout(function(){
10097 //                            try{c.dom.firstChild.focus();}catch(e){}
10098 //                       }, 50);
10099 //                       return false;
10100 //                   }
10101 //                   return true;
10102 //                });
10103 //                return;
10104 //            }
10105 //        }
10106         
10107         var days = date.getDaysInMonth();
10108         
10109         var firstOfMonth = date.getFirstDateOfMonth();
10110         var startingPos = firstOfMonth.getDay()-this.startDay;
10111         
10112         if(startingPos < this.startDay){
10113             startingPos += 7;
10114         }
10115         
10116         var pm = date.add(Date.MONTH, -1);
10117         var prevStart = pm.getDaysInMonth()-startingPos;
10118 //        
10119         this.cells = this.el.select('.fc-day',true);
10120         this.textNodes = this.el.query('.fc-day-number');
10121         this.cells.addClassOnOver('fc-state-hover');
10122         
10123         var cells = this.cells.elements;
10124         var textEls = this.textNodes;
10125         
10126         Roo.each(cells, function(cell){
10127             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10128         });
10129         
10130         days += startingPos;
10131
10132         // convert everything to numbers so it's fast
10133         var day = 86400000;
10134         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10135         //Roo.log(d);
10136         //Roo.log(pm);
10137         //Roo.log(prevStart);
10138         
10139         var today = new Date().clearTime().getTime();
10140         var sel = date.clearTime().getTime();
10141         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10142         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10143         var ddMatch = this.disabledDatesRE;
10144         var ddText = this.disabledDatesText;
10145         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10146         var ddaysText = this.disabledDaysText;
10147         var format = this.format;
10148         
10149         var setCellClass = function(cal, cell){
10150             
10151             //Roo.log('set Cell Class');
10152             cell.title = "";
10153             var t = d.getTime();
10154             
10155             //Roo.log(d);
10156             
10157             cell.dateValue = t;
10158             if(t == today){
10159                 cell.className += " fc-today";
10160                 cell.className += " fc-state-highlight";
10161                 cell.title = cal.todayText;
10162             }
10163             if(t == sel){
10164                 // disable highlight in other month..
10165                 //cell.className += " fc-state-highlight";
10166                 
10167             }
10168             // disabling
10169             if(t < min) {
10170                 cell.className = " fc-state-disabled";
10171                 cell.title = cal.minText;
10172                 return;
10173             }
10174             if(t > max) {
10175                 cell.className = " fc-state-disabled";
10176                 cell.title = cal.maxText;
10177                 return;
10178             }
10179             if(ddays){
10180                 if(ddays.indexOf(d.getDay()) != -1){
10181                     cell.title = ddaysText;
10182                     cell.className = " fc-state-disabled";
10183                 }
10184             }
10185             if(ddMatch && format){
10186                 var fvalue = d.dateFormat(format);
10187                 if(ddMatch.test(fvalue)){
10188                     cell.title = ddText.replace("%0", fvalue);
10189                     cell.className = " fc-state-disabled";
10190                 }
10191             }
10192             
10193             if (!cell.initialClassName) {
10194                 cell.initialClassName = cell.dom.className;
10195             }
10196             
10197             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10198         };
10199
10200         var i = 0;
10201         
10202         for(; i < startingPos; i++) {
10203             textEls[i].innerHTML = (++prevStart);
10204             d.setDate(d.getDate()+1);
10205             
10206             cells[i].className = "fc-past fc-other-month";
10207             setCellClass(this, cells[i]);
10208         }
10209         
10210         var intDay = 0;
10211         
10212         for(; i < days; i++){
10213             intDay = i - startingPos + 1;
10214             textEls[i].innerHTML = (intDay);
10215             d.setDate(d.getDate()+1);
10216             
10217             cells[i].className = ''; // "x-date-active";
10218             setCellClass(this, cells[i]);
10219         }
10220         var extraDays = 0;
10221         
10222         for(; i < 42; i++) {
10223             textEls[i].innerHTML = (++extraDays);
10224             d.setDate(d.getDate()+1);
10225             
10226             cells[i].className = "fc-future fc-other-month";
10227             setCellClass(this, cells[i]);
10228         }
10229         
10230         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10231         
10232         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10233         
10234         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10235         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10236         
10237         if(totalRows != 6){
10238             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10239             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10240         }
10241         
10242         this.fireEvent('monthchange', this, date);
10243         
10244         
10245         /*
10246         if(!this.internalRender){
10247             var main = this.el.dom.firstChild;
10248             var w = main.offsetWidth;
10249             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10250             Roo.fly(main).setWidth(w);
10251             this.internalRender = true;
10252             // opera does not respect the auto grow header center column
10253             // then, after it gets a width opera refuses to recalculate
10254             // without a second pass
10255             if(Roo.isOpera && !this.secondPass){
10256                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10257                 this.secondPass = true;
10258                 this.update.defer(10, this, [date]);
10259             }
10260         }
10261         */
10262         
10263     },
10264     
10265     findCell : function(dt) {
10266         dt = dt.clearTime().getTime();
10267         var ret = false;
10268         this.cells.each(function(c){
10269             //Roo.log("check " +c.dateValue + '?=' + dt);
10270             if(c.dateValue == dt){
10271                 ret = c;
10272                 return false;
10273             }
10274             return true;
10275         });
10276         
10277         return ret;
10278     },
10279     
10280     findCells : function(ev) {
10281         var s = ev.start.clone().clearTime().getTime();
10282        // Roo.log(s);
10283         var e= ev.end.clone().clearTime().getTime();
10284        // Roo.log(e);
10285         var ret = [];
10286         this.cells.each(function(c){
10287              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10288             
10289             if(c.dateValue > e){
10290                 return ;
10291             }
10292             if(c.dateValue < s){
10293                 return ;
10294             }
10295             ret.push(c);
10296         });
10297         
10298         return ret;    
10299     },
10300     
10301     findBestRow: function(cells)
10302     {
10303         var ret = 0;
10304         
10305         for (var i =0 ; i < cells.length;i++) {
10306             ret  = Math.max(cells[i].rows || 0,ret);
10307         }
10308         return ret;
10309         
10310     },
10311     
10312     
10313     addItem : function(ev)
10314     {
10315         // look for vertical location slot in
10316         var cells = this.findCells(ev);
10317         
10318         ev.row = this.findBestRow(cells);
10319         
10320         // work out the location.
10321         
10322         var crow = false;
10323         var rows = [];
10324         for(var i =0; i < cells.length; i++) {
10325             if (!crow) {
10326                 crow = {
10327                     start : cells[i],
10328                     end :  cells[i]
10329                 };
10330                 continue;
10331             }
10332             if (crow.start.getY() == cells[i].getY()) {
10333                 // on same row.
10334                 crow.end = cells[i];
10335                 continue;
10336             }
10337             // different row.
10338             rows.push(crow);
10339             crow = {
10340                 start: cells[i],
10341                 end : cells[i]
10342             };
10343             
10344         }
10345         
10346         rows.push(crow);
10347         ev.els = [];
10348         ev.rows = rows;
10349         ev.cells = cells;
10350         for (var i = 0; i < cells.length;i++) {
10351             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10352             
10353         }
10354         
10355         this.calevents.push(ev);
10356     },
10357     
10358     clearEvents: function() {
10359         
10360         if(!this.calevents){
10361             return;
10362         }
10363         
10364         Roo.each(this.cells.elements, function(c){
10365             c.rows = 0;
10366         });
10367         
10368         Roo.each(this.calevents, function(e) {
10369             Roo.each(e.els, function(el) {
10370                 el.un('mouseenter' ,this.onEventEnter, this);
10371                 el.un('mouseleave' ,this.onEventLeave, this);
10372                 el.remove();
10373             },this);
10374         },this);
10375         
10376     },
10377     
10378     renderEvents: function()
10379     {   
10380         // first make sure there is enough space..
10381         
10382         this.cells.each(function(c) {
10383         
10384             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10385         });
10386         
10387         for (var e = 0; e < this.calevents.length; e++) {
10388             var ev = this.calevents[e];
10389             var cells = ev.cells;
10390             var rows = ev.rows;
10391             
10392             for(var i =0; i < rows.length; i++) {
10393                 
10394                  
10395                 // how many rows should it span..
10396                 
10397                 var  cfg = {
10398                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10399                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10400                     
10401                     unselectable : "on",
10402                     cn : [
10403                         {
10404                             cls: 'fc-event-inner',
10405                             cn : [
10406 //                                {
10407 //                                  tag:'span',
10408 //                                  cls: 'fc-event-time',
10409 //                                  html : cells.length > 1 ? '' : ev.time
10410 //                                },
10411                                 {
10412                                   tag:'span',
10413                                   cls: 'fc-event-title',
10414                                   html : String.format('{0}', ev.title)
10415                                 }
10416                                 
10417                                 
10418                             ]
10419                         },
10420                         {
10421                             cls: 'ui-resizable-handle ui-resizable-e',
10422                             html : '&nbsp;&nbsp;&nbsp'
10423                         }
10424                         
10425                     ]
10426                 };
10427                 if (i == 0) {
10428                     cfg.cls += ' fc-event-start';
10429                 }
10430                 if ((i+1) == rows.length) {
10431                     cfg.cls += ' fc-event-end';
10432                 }
10433                 
10434                 var ctr = this.el.select('.fc-event-container',true).first();
10435                 var cg = ctr.createChild(cfg);
10436                 
10437                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10438                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10439                 cg.on('click', this.onEventClick, this, ev);
10440                 
10441                 ev.els.push(cg);
10442                 
10443                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10444                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10445                 //Roo.log(cg);
10446                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10447                 cg.setWidth(ebox.right - sbox.x -2);
10448             }
10449             
10450             
10451         }
10452         
10453     },
10454     
10455     onEventEnter: function (e, el,event,d) {
10456         this.fireEvent('evententer', this, el, event);
10457     },
10458     
10459     onEventLeave: function (e, el,event,d) {
10460         this.fireEvent('eventleave', this, el, event);
10461     },
10462     
10463     onEventClick: function (e, el,event,d) {
10464         this.fireEvent('eventclick', this, el, event);
10465     },
10466     
10467     onMonthChange: function () {
10468         this.store.load();
10469     },
10470     
10471     onLoad: function () 
10472     {   
10473         this.calevents = [];
10474         var cal = this;
10475         
10476         if(this.store.getCount() > 0){
10477             this.store.data.each(function(d){
10478                cal.addItem({
10479                     id : d.data.id,
10480                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10481                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10482                     time : d.data.start_time,
10483                     title : d.data.title,
10484                     description : d.data.description,
10485                     venue : d.data.venue
10486                 });
10487             });
10488         }
10489         
10490         this.renderEvents();
10491         
10492         if(this.loadMask){
10493             this.maskEl.hide();
10494         }
10495     },
10496     
10497     onBeforeLoad: function()
10498     {
10499         this.clearEvents();
10500         
10501         if(this.loadMask){
10502             this.maskEl.show();
10503         }
10504     }
10505 });
10506
10507  
10508  /*
10509  * - LGPL
10510  *
10511  * element
10512  * 
10513  */
10514
10515 /**
10516  * @class Roo.bootstrap.Popover
10517  * @extends Roo.bootstrap.Component
10518  * Bootstrap Popover class
10519  * @cfg {String} html contents of the popover   (or false to use children..)
10520  * @cfg {String} title of popover (or false to hide)
10521  * @cfg {String} placement how it is placed
10522  * @cfg {String} trigger click || hover (or false to trigger manually)
10523  * @cfg {String} over what (parent or false to trigger manually.)
10524  * 
10525  * @constructor
10526  * Create a new Popover
10527  * @param {Object} config The config object
10528  */
10529
10530 Roo.bootstrap.Popover = function(config){
10531     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10532 };
10533
10534 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10535     
10536     title: 'Fill in a title',
10537     html: false,
10538     
10539     placement : 'right',
10540     trigger : 'hover', // hover
10541     
10542     over: 'parent',
10543     
10544     can_build_overlaid : false,
10545     
10546     getChildContainer : function()
10547     {
10548         return this.el.select('.popover-content',true).first();
10549     },
10550     
10551     getAutoCreate : function(){
10552          Roo.log('make popover?');
10553         var cfg = {
10554            cls : 'popover roo-dynamic',
10555            style: 'display:block',
10556            cn : [
10557                 {
10558                     cls : 'arrow'
10559                 },
10560                 {
10561                     cls : 'popover-inner',
10562                     cn : [
10563                         {
10564                             tag: 'h3',
10565                             cls: 'popover-title',
10566                             html : this.title
10567                         },
10568                         {
10569                             cls : 'popover-content',
10570                             html : this.html
10571                         }
10572                     ]
10573                     
10574                 }
10575            ]
10576         };
10577         
10578         return cfg;
10579     },
10580     setTitle: function(str)
10581     {
10582         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10583     },
10584     setContent: function(str)
10585     {
10586         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10587     },
10588     // as it get's added to the bottom of the page.
10589     onRender : function(ct, position)
10590     {
10591         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10592         if(!this.el){
10593             var cfg = Roo.apply({},  this.getAutoCreate());
10594             cfg.id = Roo.id();
10595             
10596             if (this.cls) {
10597                 cfg.cls += ' ' + this.cls;
10598             }
10599             if (this.style) {
10600                 cfg.style = this.style;
10601             }
10602             Roo.log("adding to ")
10603             this.el = Roo.get(document.body).createChild(cfg, position);
10604             Roo.log(this.el);
10605         }
10606         this.initEvents();
10607     },
10608     
10609     initEvents : function()
10610     {
10611         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10612         this.el.enableDisplayMode('block');
10613         this.el.hide();
10614         if (this.over === false) {
10615             return; 
10616         }
10617         if (this.triggers === false) {
10618             return;
10619         }
10620         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10621         var triggers = this.trigger ? this.trigger.split(' ') : [];
10622         Roo.each(triggers, function(trigger) {
10623         
10624             if (trigger == 'click') {
10625                 on_el.on('click', this.toggle, this);
10626             } else if (trigger != 'manual') {
10627                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10628                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10629       
10630                 on_el.on(eventIn  ,this.enter, this);
10631                 on_el.on(eventOut, this.leave, this);
10632             }
10633         }, this);
10634         
10635     },
10636     
10637     
10638     // private
10639     timeout : null,
10640     hoverState : null,
10641     
10642     toggle : function () {
10643         this.hoverState == 'in' ? this.leave() : this.enter();
10644     },
10645     
10646     enter : function () {
10647        
10648     
10649         clearTimeout(this.timeout);
10650     
10651         this.hoverState = 'in'
10652     
10653         if (!this.delay || !this.delay.show) {
10654             this.show();
10655             return 
10656         }
10657         var _t = this;
10658         this.timeout = setTimeout(function () {
10659             if (_t.hoverState == 'in') {
10660                 _t.show();
10661             }
10662         }, this.delay.show)
10663     },
10664     leave : function() {
10665         clearTimeout(this.timeout);
10666     
10667         this.hoverState = 'out'
10668     
10669         if (!this.delay || !this.delay.hide) {
10670             this.hide();
10671             return 
10672         }
10673         var _t = this;
10674         this.timeout = setTimeout(function () {
10675             if (_t.hoverState == 'out') {
10676                 _t.hide();
10677             }
10678         }, this.delay.hide)
10679     },
10680     
10681     show : function (on_el)
10682     {
10683         if (!on_el) {
10684             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10685         }
10686         // set content.
10687         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10688         if (this.html !== false) {
10689             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10690         }
10691         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10692         if (!this.title.length) {
10693             this.el.select('.popover-title',true).hide();
10694         }
10695         
10696         var placement = typeof this.placement == 'function' ?
10697             this.placement.call(this, this.el, on_el) :
10698             this.placement;
10699             
10700         var autoToken = /\s?auto?\s?/i;
10701         var autoPlace = autoToken.test(placement);
10702         if (autoPlace) {
10703             placement = placement.replace(autoToken, '') || 'top';
10704         }
10705         
10706         //this.el.detach()
10707         //this.el.setXY([0,0]);
10708         this.el.show();
10709         this.el.dom.style.display='block';
10710         this.el.addClass(placement);
10711         
10712         //this.el.appendTo(on_el);
10713         
10714         var p = this.getPosition();
10715         var box = this.el.getBox();
10716         
10717         if (autoPlace) {
10718             // fixme..
10719         }
10720         var align = Roo.bootstrap.Popover.alignment[placement]
10721         this.el.alignTo(on_el, align[0],align[1]);
10722         //var arrow = this.el.select('.arrow',true).first();
10723         //arrow.set(align[2], 
10724         
10725         this.el.addClass('in');
10726         this.hoverState = null;
10727         
10728         if (this.el.hasClass('fade')) {
10729             // fade it?
10730         }
10731         
10732     },
10733     hide : function()
10734     {
10735         this.el.setXY([0,0]);
10736         this.el.removeClass('in');
10737         this.el.hide();
10738         
10739     }
10740     
10741 });
10742
10743 Roo.bootstrap.Popover.alignment = {
10744     'left' : ['r-l', [-10,0], 'right'],
10745     'right' : ['l-r', [10,0], 'left'],
10746     'bottom' : ['t-b', [0,10], 'top'],
10747     'top' : [ 'b-t', [0,-10], 'bottom']
10748 };
10749
10750  /*
10751  * - LGPL
10752  *
10753  * Progress
10754  * 
10755  */
10756
10757 /**
10758  * @class Roo.bootstrap.Progress
10759  * @extends Roo.bootstrap.Component
10760  * Bootstrap Progress class
10761  * @cfg {Boolean} striped striped of the progress bar
10762  * @cfg {Boolean} active animated of the progress bar
10763  * 
10764  * 
10765  * @constructor
10766  * Create a new Progress
10767  * @param {Object} config The config object
10768  */
10769
10770 Roo.bootstrap.Progress = function(config){
10771     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10772 };
10773
10774 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10775     
10776     striped : false,
10777     active: false,
10778     
10779     getAutoCreate : function(){
10780         var cfg = {
10781             tag: 'div',
10782             cls: 'progress'
10783         };
10784         
10785         
10786         if(this.striped){
10787             cfg.cls += ' progress-striped';
10788         }
10789       
10790         if(this.active){
10791             cfg.cls += ' active';
10792         }
10793         
10794         
10795         return cfg;
10796     }
10797    
10798 });
10799
10800  
10801
10802  /*
10803  * - LGPL
10804  *
10805  * ProgressBar
10806  * 
10807  */
10808
10809 /**
10810  * @class Roo.bootstrap.ProgressBar
10811  * @extends Roo.bootstrap.Component
10812  * Bootstrap ProgressBar class
10813  * @cfg {Number} aria_valuenow aria-value now
10814  * @cfg {Number} aria_valuemin aria-value min
10815  * @cfg {Number} aria_valuemax aria-value max
10816  * @cfg {String} label label for the progress bar
10817  * @cfg {String} panel (success | info | warning | danger )
10818  * @cfg {String} role role of the progress bar
10819  * @cfg {String} sr_only text
10820  * 
10821  * 
10822  * @constructor
10823  * Create a new ProgressBar
10824  * @param {Object} config The config object
10825  */
10826
10827 Roo.bootstrap.ProgressBar = function(config){
10828     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10829 };
10830
10831 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10832     
10833     aria_valuenow : 0,
10834     aria_valuemin : 0,
10835     aria_valuemax : 100,
10836     label : false,
10837     panel : false,
10838     role : false,
10839     sr_only: false,
10840     
10841     getAutoCreate : function()
10842     {
10843         
10844         var cfg = {
10845             tag: 'div',
10846             cls: 'progress-bar',
10847             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10848         };
10849         
10850         if(this.sr_only){
10851             cfg.cn = {
10852                 tag: 'span',
10853                 cls: 'sr-only',
10854                 html: this.sr_only
10855             }
10856         }
10857         
10858         if(this.role){
10859             cfg.role = this.role;
10860         }
10861         
10862         if(this.aria_valuenow){
10863             cfg['aria-valuenow'] = this.aria_valuenow;
10864         }
10865         
10866         if(this.aria_valuemin){
10867             cfg['aria-valuemin'] = this.aria_valuemin;
10868         }
10869         
10870         if(this.aria_valuemax){
10871             cfg['aria-valuemax'] = this.aria_valuemax;
10872         }
10873         
10874         if(this.label && !this.sr_only){
10875             cfg.html = this.label;
10876         }
10877         
10878         if(this.panel){
10879             cfg.cls += ' progress-bar-' + this.panel;
10880         }
10881         
10882         return cfg;
10883     },
10884     
10885     update : function(aria_valuenow)
10886     {
10887         this.aria_valuenow = aria_valuenow;
10888         
10889         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10890     }
10891    
10892 });
10893
10894  
10895
10896  /*
10897  * - LGPL
10898  *
10899  * TabPanel
10900  * 
10901  */
10902
10903 /**
10904  * @class Roo.bootstrap.TabPanel
10905  * @extends Roo.bootstrap.Component
10906  * Bootstrap TabPanel class
10907  * @cfg {Boolean} active panel active
10908  * @cfg {String} html panel content
10909  * @cfg {String} tabId tab relate id
10910  * 
10911  * 
10912  * @constructor
10913  * Create a new TabPanel
10914  * @param {Object} config The config object
10915  */
10916
10917 Roo.bootstrap.TabPanel = function(config){
10918     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10919 };
10920
10921 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10922     
10923     active: false,
10924     html: false,
10925     tabId: false,
10926     
10927     getAutoCreate : function(){
10928         var cfg = {
10929             tag: 'div',
10930             cls: 'tab-pane',
10931             html: this.html || ''
10932         };
10933         
10934         if(this.active){
10935             cfg.cls += ' active';
10936         }
10937         
10938         if(this.tabId){
10939             cfg.tabId = this.tabId;
10940         }
10941         
10942         return cfg;
10943     }
10944    
10945 });
10946
10947  
10948
10949  /*
10950  * - LGPL
10951  *
10952  * DateField
10953  * 
10954  */
10955
10956 /**
10957  * @class Roo.bootstrap.DateField
10958  * @extends Roo.bootstrap.Input
10959  * Bootstrap DateField class
10960  * @cfg {Number} weekStart default 0
10961  * @cfg {Number} weekStart default 0
10962  * @cfg {Number} viewMode default empty, (months|years)
10963  * @cfg {Number} minViewMode default empty, (months|years)
10964  * @cfg {Number} startDate default -Infinity
10965  * @cfg {Number} endDate default Infinity
10966  * @cfg {Boolean} todayHighlight default false
10967  * @cfg {Boolean} todayBtn default false
10968  * @cfg {Boolean} calendarWeeks default false
10969  * @cfg {Object} daysOfWeekDisabled default empty
10970  * 
10971  * @cfg {Boolean} keyboardNavigation default true
10972  * @cfg {String} language default en
10973  * 
10974  * @constructor
10975  * Create a new DateField
10976  * @param {Object} config The config object
10977  */
10978
10979 Roo.bootstrap.DateField = function(config){
10980     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10981      this.addEvents({
10982             /**
10983              * @event show
10984              * Fires when this field show.
10985              * @param {Roo.bootstrap.DateField} this
10986              * @param {Mixed} date The date value
10987              */
10988             show : true,
10989             /**
10990              * @event show
10991              * Fires when this field hide.
10992              * @param {Roo.bootstrap.DateField} this
10993              * @param {Mixed} date The date value
10994              */
10995             hide : true,
10996             /**
10997              * @event select
10998              * Fires when select a date.
10999              * @param {Roo.bootstrap.DateField} this
11000              * @param {Mixed} date The date value
11001              */
11002             select : true
11003         });
11004 };
11005
11006 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11007     
11008     /**
11009      * @cfg {String} format
11010      * The default date format string which can be overriden for localization support.  The format must be
11011      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11012      */
11013     format : "m/d/y",
11014     /**
11015      * @cfg {String} altFormats
11016      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11017      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11018      */
11019     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11020     
11021     weekStart : 0,
11022     
11023     viewMode : '',
11024     
11025     minViewMode : '',
11026     
11027     todayHighlight : false,
11028     
11029     todayBtn: false,
11030     
11031     language: 'en',
11032     
11033     keyboardNavigation: true,
11034     
11035     calendarWeeks: false,
11036     
11037     startDate: -Infinity,
11038     
11039     endDate: Infinity,
11040     
11041     daysOfWeekDisabled: [],
11042     
11043     _events: [],
11044     
11045     UTCDate: function()
11046     {
11047         return new Date(Date.UTC.apply(Date, arguments));
11048     },
11049     
11050     UTCToday: function()
11051     {
11052         var today = new Date();
11053         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11054     },
11055     
11056     getDate: function() {
11057             var d = this.getUTCDate();
11058             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11059     },
11060     
11061     getUTCDate: function() {
11062             return this.date;
11063     },
11064     
11065     setDate: function(d) {
11066             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11067     },
11068     
11069     setUTCDate: function(d) {
11070             this.date = d;
11071             this.setValue(this.formatDate(this.date));
11072     },
11073         
11074     onRender: function(ct, position)
11075     {
11076         
11077         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11078         
11079         this.language = this.language || 'en';
11080         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11081         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11082         
11083         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11084         this.format = this.format || 'm/d/y';
11085         this.isInline = false;
11086         this.isInput = true;
11087         this.component = this.el.select('.add-on', true).first() || false;
11088         this.component = (this.component && this.component.length === 0) ? false : this.component;
11089         this.hasInput = this.component && this.inputEL().length;
11090         
11091         if (typeof(this.minViewMode === 'string')) {
11092             switch (this.minViewMode) {
11093                 case 'months':
11094                     this.minViewMode = 1;
11095                     break;
11096                 case 'years':
11097                     this.minViewMode = 2;
11098                     break;
11099                 default:
11100                     this.minViewMode = 0;
11101                     break;
11102             }
11103         }
11104         
11105         if (typeof(this.viewMode === 'string')) {
11106             switch (this.viewMode) {
11107                 case 'months':
11108                     this.viewMode = 1;
11109                     break;
11110                 case 'years':
11111                     this.viewMode = 2;
11112                     break;
11113                 default:
11114                     this.viewMode = 0;
11115                     break;
11116             }
11117         }
11118                 
11119         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11120         
11121         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11122         
11123         this.picker().on('mousedown', this.onMousedown, this);
11124         this.picker().on('click', this.onClick, this);
11125         
11126         this.picker().addClass('datepicker-dropdown');
11127         
11128         this.startViewMode = this.viewMode;
11129         
11130         
11131         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11132             if(!this.calendarWeeks){
11133                 v.remove();
11134                 return;
11135             };
11136             
11137             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11138             v.attr('colspan', function(i, val){
11139                 return parseInt(val) + 1;
11140             });
11141         })
11142                         
11143         
11144         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11145         
11146         this.setStartDate(this.startDate);
11147         this.setEndDate(this.endDate);
11148         
11149         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11150         
11151         this.fillDow();
11152         this.fillMonths();
11153         this.update();
11154         this.showMode();
11155         
11156         if(this.isInline) {
11157             this.show();
11158         }
11159     },
11160     
11161     picker : function()
11162     {
11163         return this.el.select('.datepicker', true).first();
11164     },
11165     
11166     fillDow: function()
11167     {
11168         var dowCnt = this.weekStart;
11169         
11170         var dow = {
11171             tag: 'tr',
11172             cn: [
11173                 
11174             ]
11175         };
11176         
11177         if(this.calendarWeeks){
11178             dow.cn.push({
11179                 tag: 'th',
11180                 cls: 'cw',
11181                 html: '&nbsp;'
11182             })
11183         }
11184         
11185         while (dowCnt < this.weekStart + 7) {
11186             dow.cn.push({
11187                 tag: 'th',
11188                 cls: 'dow',
11189                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11190             });
11191         }
11192         
11193         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11194     },
11195     
11196     fillMonths: function()
11197     {    
11198         var i = 0
11199         var months = this.picker().select('>.datepicker-months td', true).first();
11200         
11201         months.dom.innerHTML = '';
11202         
11203         while (i < 12) {
11204             var month = {
11205                 tag: 'span',
11206                 cls: 'month',
11207                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11208             }
11209             
11210             months.createChild(month);
11211         }
11212         
11213     },
11214     
11215     update: function(){
11216         
11217         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11218         
11219         if (this.date < this.startDate) {
11220             this.viewDate = new Date(this.startDate);
11221         } else if (this.date > this.endDate) {
11222             this.viewDate = new Date(this.endDate);
11223         } else {
11224             this.viewDate = new Date(this.date);
11225         }
11226         
11227         this.fill();
11228     },
11229     
11230     fill: function() {
11231         var d = new Date(this.viewDate),
11232                 year = d.getUTCFullYear(),
11233                 month = d.getUTCMonth(),
11234                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11235                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11236                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11237                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11238                 currentDate = this.date && this.date.valueOf(),
11239                 today = this.UTCToday();
11240         
11241         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11242         
11243 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11244         
11245 //        this.picker.select('>tfoot th.today').
11246 //                                              .text(dates[this.language].today)
11247 //                                              .toggle(this.todayBtn !== false);
11248     
11249         this.updateNavArrows();
11250         this.fillMonths();
11251                                                 
11252         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11253         
11254         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11255          
11256         prevMonth.setUTCDate(day);
11257         
11258         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11259         
11260         var nextMonth = new Date(prevMonth);
11261         
11262         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11263         
11264         nextMonth = nextMonth.valueOf();
11265         
11266         var fillMonths = false;
11267         
11268         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11269         
11270         while(prevMonth.valueOf() < nextMonth) {
11271             var clsName = '';
11272             
11273             if (prevMonth.getUTCDay() === this.weekStart) {
11274                 if(fillMonths){
11275                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11276                 }
11277                     
11278                 fillMonths = {
11279                     tag: 'tr',
11280                     cn: []
11281                 };
11282                 
11283                 if(this.calendarWeeks){
11284                     // ISO 8601: First week contains first thursday.
11285                     // ISO also states week starts on Monday, but we can be more abstract here.
11286                     var
11287                     // Start of current week: based on weekstart/current date
11288                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11289                     // Thursday of this week
11290                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11291                     // First Thursday of year, year from thursday
11292                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11293                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11294                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11295                     
11296                     fillMonths.cn.push({
11297                         tag: 'td',
11298                         cls: 'cw',
11299                         html: calWeek
11300                     });
11301                 }
11302             }
11303             
11304             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11305                 clsName += ' old';
11306             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11307                 clsName += ' new';
11308             }
11309             if (this.todayHighlight &&
11310                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11311                 prevMonth.getUTCMonth() == today.getMonth() &&
11312                 prevMonth.getUTCDate() == today.getDate()) {
11313                 clsName += ' today';
11314             }
11315             
11316             if (currentDate && prevMonth.valueOf() === currentDate) {
11317                 clsName += ' active';
11318             }
11319             
11320             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11321                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11322                     clsName += ' disabled';
11323             }
11324             
11325             fillMonths.cn.push({
11326                 tag: 'td',
11327                 cls: 'day ' + clsName,
11328                 html: prevMonth.getDate()
11329             })
11330             
11331             prevMonth.setDate(prevMonth.getDate()+1);
11332         }
11333           
11334         var currentYear = this.date && this.date.getUTCFullYear();
11335         var currentMonth = this.date && this.date.getUTCMonth();
11336         
11337         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11338         
11339         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11340             v.removeClass('active');
11341             
11342             if(currentYear === year && k === currentMonth){
11343                 v.addClass('active');
11344             }
11345             
11346             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11347                 v.addClass('disabled');
11348             }
11349             
11350         });
11351         
11352         
11353         year = parseInt(year/10, 10) * 10;
11354         
11355         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11356         
11357         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11358         
11359         year -= 1;
11360         for (var i = -1; i < 11; i++) {
11361             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11362                 tag: 'span',
11363                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11364                 html: year
11365             })
11366             
11367             year += 1;
11368         }
11369     },
11370     
11371     showMode: function(dir) {
11372         if (dir) {
11373             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11374         }
11375         Roo.each(this.picker().select('>div',true).elements, function(v){
11376             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11377             v.hide();
11378         });
11379         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11380     },
11381     
11382     place: function()
11383     {
11384         if(this.isInline) return;
11385         
11386         this.picker().removeClass(['bottom', 'top']);
11387         
11388         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11389             /*
11390              * place to the top of element!
11391              *
11392              */
11393             
11394             this.picker().addClass('top');
11395             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11396             
11397             return;
11398         }
11399         
11400         this.picker().addClass('bottom');
11401         
11402         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11403     },
11404     
11405     parseDate : function(value){
11406         if(!value || value instanceof Date){
11407             return value;
11408         }
11409         var v = Date.parseDate(value, this.format);
11410         if (!v && this.useIso) {
11411             v = Date.parseDate(value, 'Y-m-d');
11412         }
11413         if(!v && this.altFormats){
11414             if(!this.altFormatsArray){
11415                 this.altFormatsArray = this.altFormats.split("|");
11416             }
11417             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11418                 v = Date.parseDate(value, this.altFormatsArray[i]);
11419             }
11420         }
11421         return v;
11422     },
11423     
11424     formatDate : function(date, fmt){
11425         return (!date || !(date instanceof Date)) ?
11426         date : date.dateFormat(fmt || this.format);
11427     },
11428     
11429     onFocus : function()
11430     {
11431         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11432         this.show();
11433     },
11434     
11435     onBlur : function()
11436     {
11437         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11438         this.hide();
11439     },
11440     
11441     show : function()
11442     {
11443         this.picker().show();
11444         this.update();
11445         this.place();
11446         
11447         this.fireEvent('show', this, this.date);
11448     },
11449     
11450     hide : function()
11451     {
11452         if(this.isInline) return;
11453         this.picker().hide();
11454         this.viewMode = this.startViewMode;
11455         this.showMode();
11456         
11457         this.fireEvent('hide', this, this.date);
11458         
11459     },
11460     
11461     onMousedown: function(e){
11462         e.stopPropagation();
11463         e.preventDefault();
11464     },
11465     
11466     keyup: function(e){
11467         Roo.bootstrap.DateField.superclass.keyup.call(this);
11468         this.update();
11469         
11470     },
11471
11472     setValue: function(v){
11473         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11474         
11475         this.fireEvent('select', this, this.date);
11476         
11477     },
11478     
11479     fireKey: function(e){
11480         if (!this.picker().isVisible()){
11481             if (e.keyCode == 27) // allow escape to hide and re-show picker
11482                 this.show();
11483             return;
11484         }
11485         var dateChanged = false,
11486         dir, day, month,
11487         newDate, newViewDate;
11488         switch(e.keyCode){
11489             case 27: // escape
11490                 this.hide();
11491                 e.preventDefault();
11492                 break;
11493             case 37: // left
11494             case 39: // right
11495                 if (!this.keyboardNavigation) break;
11496                 dir = e.keyCode == 37 ? -1 : 1;
11497                 
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);
11507                     newViewDate = new Date(this.viewDate);
11508                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
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 38: // up
11520             case 40: // down
11521                 if (!this.keyboardNavigation) break;
11522                 dir = e.keyCode == 38 ? -1 : 1;
11523                 if (e.ctrlKey){
11524                     newDate = this.moveYear(this.date, dir);
11525                     newViewDate = this.moveYear(this.viewDate, dir);
11526                 } else if (e.shiftKey){
11527                     newDate = this.moveMonth(this.date, dir);
11528                     newViewDate = this.moveMonth(this.viewDate, dir);
11529                 } else {
11530                     newDate = new Date(this.date);
11531                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11532                     newViewDate = new Date(this.viewDate);
11533                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11534                 }
11535                 if (this.dateWithinRange(newDate)){
11536                     this.date = newDate;
11537                     this.viewDate = newViewDate;
11538                     this.setValue(this.formatDate(this.date));
11539                     this.update();
11540                     e.preventDefault();
11541                     dateChanged = true;
11542                 }
11543                 break;
11544             case 13: // enter
11545                 this.setValue(this.formatDate(this.date));
11546                 this.hide();
11547                 e.preventDefault();
11548                 break;
11549             case 9: // tab
11550                 this.setValue(this.formatDate(this.date));
11551                 this.hide();
11552                 break;
11553         }
11554     },
11555     
11556     
11557     onClick: function(e) {
11558         e.stopPropagation();
11559         e.preventDefault();
11560         
11561         var target = e.getTarget();
11562         
11563         if(target.nodeName.toLowerCase() === 'i'){
11564             target = Roo.get(target).dom.parentNode;
11565         }
11566         
11567         var nodeName = target.nodeName;
11568         var className = target.className;
11569         var html = target.innerHTML;
11570         
11571         switch(nodeName.toLowerCase()) {
11572             case 'th':
11573                 switch(className) {
11574                     case 'switch':
11575                         this.showMode(1);
11576                         break;
11577                     case 'prev':
11578                     case 'next':
11579                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11580                         switch(this.viewMode){
11581                                 case 0:
11582                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11583                                         break;
11584                                 case 1:
11585                                 case 2:
11586                                         this.viewDate = this.moveYear(this.viewDate, dir);
11587                                         break;
11588                         }
11589                         this.fill();
11590                         break;
11591                     case 'today':
11592                         var date = new Date();
11593                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11594                         this.fill()
11595                         this.setValue(this.formatDate(this.date));
11596                         this.hide();
11597                         break;
11598                 }
11599                 break;
11600             case 'span':
11601                 if (className.indexOf('disabled') === -1) {
11602                     this.viewDate.setUTCDate(1);
11603                     if (className.indexOf('month') !== -1) {
11604                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11605                     } else {
11606                         var year = parseInt(html, 10) || 0;
11607                         this.viewDate.setUTCFullYear(year);
11608                         
11609                     }
11610                     this.showMode(-1);
11611                     this.fill();
11612                 }
11613                 break;
11614                 
11615             case 'td':
11616                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11617                     var day = parseInt(html, 10) || 1;
11618                     var year = this.viewDate.getUTCFullYear(),
11619                         month = this.viewDate.getUTCMonth();
11620
11621                     if (className.indexOf('old') !== -1) {
11622                         if(month === 0 ){
11623                             month = 11;
11624                             year -= 1;
11625                         }else{
11626                             month -= 1;
11627                         }
11628                     } else if (className.indexOf('new') !== -1) {
11629                         if (month == 11) {
11630                             month = 0;
11631                             year += 1;
11632                         } else {
11633                             month += 1;
11634                         }
11635                     }
11636                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11637                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11638                     this.fill();
11639                     this.setValue(this.formatDate(this.date));
11640                     this.hide();
11641                 }
11642                 break;
11643         }
11644     },
11645     
11646     setStartDate: function(startDate){
11647         this.startDate = startDate || -Infinity;
11648         if (this.startDate !== -Infinity) {
11649             this.startDate = this.parseDate(this.startDate);
11650         }
11651         this.update();
11652         this.updateNavArrows();
11653     },
11654
11655     setEndDate: function(endDate){
11656         this.endDate = endDate || Infinity;
11657         if (this.endDate !== Infinity) {
11658             this.endDate = this.parseDate(this.endDate);
11659         }
11660         this.update();
11661         this.updateNavArrows();
11662     },
11663     
11664     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11665         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11666         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11667             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11668         }
11669         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11670             return parseInt(d, 10);
11671         });
11672         this.update();
11673         this.updateNavArrows();
11674     },
11675     
11676     updateNavArrows: function() {
11677         var d = new Date(this.viewDate),
11678         year = d.getUTCFullYear(),
11679         month = d.getUTCMonth();
11680         
11681         Roo.each(this.picker().select('.prev', true).elements, function(v){
11682             v.show();
11683             switch (this.viewMode) {
11684                 case 0:
11685
11686                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11687                         v.hide();
11688                     }
11689                     break;
11690                 case 1:
11691                 case 2:
11692                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11693                         v.hide();
11694                     }
11695                     break;
11696             }
11697         });
11698         
11699         Roo.each(this.picker().select('.next', true).elements, function(v){
11700             v.show();
11701             switch (this.viewMode) {
11702                 case 0:
11703
11704                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11705                         v.hide();
11706                     }
11707                     break;
11708                 case 1:
11709                 case 2:
11710                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11711                         v.hide();
11712                     }
11713                     break;
11714             }
11715         })
11716     },
11717     
11718     moveMonth: function(date, dir){
11719         if (!dir) return date;
11720         var new_date = new Date(date.valueOf()),
11721         day = new_date.getUTCDate(),
11722         month = new_date.getUTCMonth(),
11723         mag = Math.abs(dir),
11724         new_month, test;
11725         dir = dir > 0 ? 1 : -1;
11726         if (mag == 1){
11727             test = dir == -1
11728             // If going back one month, make sure month is not current month
11729             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11730             ? function(){
11731                 return new_date.getUTCMonth() == month;
11732             }
11733             // If going forward one month, make sure month is as expected
11734             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11735             : function(){
11736                 return new_date.getUTCMonth() != new_month;
11737             };
11738             new_month = month + dir;
11739             new_date.setUTCMonth(new_month);
11740             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11741             if (new_month < 0 || new_month > 11)
11742                 new_month = (new_month + 12) % 12;
11743         } else {
11744             // For magnitudes >1, move one month at a time...
11745             for (var i=0; i<mag; i++)
11746                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11747                 new_date = this.moveMonth(new_date, dir);
11748             // ...then reset the day, keeping it in the new month
11749             new_month = new_date.getUTCMonth();
11750             new_date.setUTCDate(day);
11751             test = function(){
11752                 return new_month != new_date.getUTCMonth();
11753             };
11754         }
11755         // Common date-resetting loop -- if date is beyond end of month, make it
11756         // end of month
11757         while (test()){
11758             new_date.setUTCDate(--day);
11759             new_date.setUTCMonth(new_month);
11760         }
11761         return new_date;
11762     },
11763
11764     moveYear: function(date, dir){
11765         return this.moveMonth(date, dir*12);
11766     },
11767
11768     dateWithinRange: function(date){
11769         return date >= this.startDate && date <= this.endDate;
11770     },
11771
11772     
11773     remove: function() {
11774         this.picker().remove();
11775     }
11776    
11777 });
11778
11779 Roo.apply(Roo.bootstrap.DateField,  {
11780     
11781     head : {
11782         tag: 'thead',
11783         cn: [
11784         {
11785             tag: 'tr',
11786             cn: [
11787             {
11788                 tag: 'th',
11789                 cls: 'prev',
11790                 html: '<i class="icon-arrow-left"/>'
11791             },
11792             {
11793                 tag: 'th',
11794                 cls: 'switch',
11795                 colspan: '5'
11796             },
11797             {
11798                 tag: 'th',
11799                 cls: 'next',
11800                 html: '<i class="icon-arrow-right"/>'
11801             }
11802
11803             ]
11804         }
11805         ]
11806     },
11807     
11808     content : {
11809         tag: 'tbody',
11810         cn: [
11811         {
11812             tag: 'tr',
11813             cn: [
11814             {
11815                 tag: 'td',
11816                 colspan: '7'
11817             }
11818             ]
11819         }
11820         ]
11821     },
11822     
11823     footer : {
11824         tag: 'tfoot',
11825         cn: [
11826         {
11827             tag: 'tr',
11828             cn: [
11829             {
11830                 tag: 'th',
11831                 colspan: '7',
11832                 cls: 'today'
11833             }
11834                     
11835             ]
11836         }
11837         ]
11838     },
11839     
11840     dates:{
11841         en: {
11842             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11843             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11844             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11845             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11846             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11847             today: "Today"
11848         }
11849     },
11850     
11851     modes: [
11852     {
11853         clsName: 'days',
11854         navFnc: 'Month',
11855         navStep: 1
11856     },
11857     {
11858         clsName: 'months',
11859         navFnc: 'FullYear',
11860         navStep: 1
11861     },
11862     {
11863         clsName: 'years',
11864         navFnc: 'FullYear',
11865         navStep: 10
11866     }]
11867 });
11868
11869 Roo.apply(Roo.bootstrap.DateField,  {
11870   
11871     template : {
11872         tag: 'div',
11873         cls: 'datepicker dropdown-menu',
11874         cn: [
11875         {
11876             tag: 'div',
11877             cls: 'datepicker-days',
11878             cn: [
11879             {
11880                 tag: 'table',
11881                 cls: 'table-condensed',
11882                 cn:[
11883                 Roo.bootstrap.DateField.head,
11884                 {
11885                     tag: 'tbody'
11886                 },
11887                 Roo.bootstrap.DateField.footer
11888                 ]
11889             }
11890             ]
11891         },
11892         {
11893             tag: 'div',
11894             cls: 'datepicker-months',
11895             cn: [
11896             {
11897                 tag: 'table',
11898                 cls: 'table-condensed',
11899                 cn:[
11900                 Roo.bootstrap.DateField.head,
11901                 Roo.bootstrap.DateField.content,
11902                 Roo.bootstrap.DateField.footer
11903                 ]
11904             }
11905             ]
11906         },
11907         {
11908             tag: 'div',
11909             cls: 'datepicker-years',
11910             cn: [
11911             {
11912                 tag: 'table',
11913                 cls: 'table-condensed',
11914                 cn:[
11915                 Roo.bootstrap.DateField.head,
11916                 Roo.bootstrap.DateField.content,
11917                 Roo.bootstrap.DateField.footer
11918                 ]
11919             }
11920             ]
11921         }
11922         ]
11923     }
11924 });
11925
11926  
11927
11928  /*
11929  * - LGPL
11930  *
11931  * TimeField
11932  * 
11933  */
11934
11935 /**
11936  * @class Roo.bootstrap.TimeField
11937  * @extends Roo.bootstrap.Input
11938  * Bootstrap DateField class
11939  * 
11940  * 
11941  * @constructor
11942  * Create a new TimeField
11943  * @param {Object} config The config object
11944  */
11945
11946 Roo.bootstrap.TimeField = function(config){
11947     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11948     this.addEvents({
11949             /**
11950              * @event show
11951              * Fires when this field show.
11952              * @param {Roo.bootstrap.DateField} this
11953              * @param {Mixed} date The date value
11954              */
11955             show : true,
11956             /**
11957              * @event show
11958              * Fires when this field hide.
11959              * @param {Roo.bootstrap.DateField} this
11960              * @param {Mixed} date The date value
11961              */
11962             hide : true,
11963             /**
11964              * @event select
11965              * Fires when select a date.
11966              * @param {Roo.bootstrap.DateField} this
11967              * @param {Mixed} date The date value
11968              */
11969             select : true
11970         });
11971 };
11972
11973 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11974     
11975     /**
11976      * @cfg {String} format
11977      * The default time format string which can be overriden for localization support.  The format must be
11978      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11979      */
11980     format : "H:i",
11981        
11982     onRender: function(ct, position)
11983     {
11984         
11985         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11986                 
11987         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11988         
11989         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11990         
11991         this.pop = this.picker().select('>.datepicker-time',true).first();
11992         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11993         
11994         this.picker().on('mousedown', this.onMousedown, this);
11995         this.picker().on('click', this.onClick, this);
11996         
11997         this.picker().addClass('datepicker-dropdown');
11998     
11999         this.fillTime();
12000         this.update();
12001             
12002         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12003         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12004         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12005         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12006         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12007         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12008
12009     },
12010     
12011     fireKey: function(e){
12012         if (!this.picker().isVisible()){
12013             if (e.keyCode == 27) // allow escape to hide and re-show picker
12014                 this.show();
12015             return;
12016         }
12017
12018         e.preventDefault();
12019         
12020         switch(e.keyCode){
12021             case 27: // escape
12022                 this.hide();
12023                 break;
12024             case 37: // left
12025             case 39: // right
12026                 this.onTogglePeriod();
12027                 break;
12028             case 38: // up
12029                 this.onIncrementMinutes();
12030                 break;
12031             case 40: // down
12032                 this.onDecrementMinutes();
12033                 break;
12034             case 13: // enter
12035             case 9: // tab
12036                 this.setTime();
12037                 break;
12038         }
12039     },
12040     
12041     onClick: function(e) {
12042         e.stopPropagation();
12043         e.preventDefault();
12044     },
12045     
12046     picker : function()
12047     {
12048         return this.el.select('.datepicker', true).first();
12049     },
12050     
12051     fillTime: function()
12052     {    
12053         var time = this.pop.select('tbody', true).first();
12054         
12055         time.dom.innerHTML = '';
12056         
12057         time.createChild({
12058             tag: 'tr',
12059             cn: [
12060                 {
12061                     tag: 'td',
12062                     cn: [
12063                         {
12064                             tag: 'a',
12065                             href: '#',
12066                             cls: 'btn',
12067                             cn: [
12068                                 {
12069                                     tag: 'span',
12070                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12071                                 }
12072                             ]
12073                         } 
12074                     ]
12075                 },
12076                 {
12077                     tag: 'td',
12078                     cls: 'separator'
12079                 },
12080                 {
12081                     tag: 'td',
12082                     cn: [
12083                         {
12084                             tag: 'a',
12085                             href: '#',
12086                             cls: 'btn',
12087                             cn: [
12088                                 {
12089                                     tag: 'span',
12090                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12091                                 }
12092                             ]
12093                         }
12094                     ]
12095                 },
12096                 {
12097                     tag: 'td',
12098                     cls: 'separator'
12099                 }
12100             ]
12101         });
12102         
12103         time.createChild({
12104             tag: 'tr',
12105             cn: [
12106                 {
12107                     tag: 'td',
12108                     cn: [
12109                         {
12110                             tag: 'span',
12111                             cls: 'timepicker-hour',
12112                             html: '00'
12113                         }  
12114                     ]
12115                 },
12116                 {
12117                     tag: 'td',
12118                     cls: 'separator',
12119                     html: ':'
12120                 },
12121                 {
12122                     tag: 'td',
12123                     cn: [
12124                         {
12125                             tag: 'span',
12126                             cls: 'timepicker-minute',
12127                             html: '00'
12128                         }  
12129                     ]
12130                 },
12131                 {
12132                     tag: 'td',
12133                     cls: 'separator'
12134                 },
12135                 {
12136                     tag: 'td',
12137                     cn: [
12138                         {
12139                             tag: 'button',
12140                             type: 'button',
12141                             cls: 'btn btn-primary period',
12142                             html: 'AM'
12143                             
12144                         }
12145                     ]
12146                 }
12147             ]
12148         });
12149         
12150         time.createChild({
12151             tag: 'tr',
12152             cn: [
12153                 {
12154                     tag: 'td',
12155                     cn: [
12156                         {
12157                             tag: 'a',
12158                             href: '#',
12159                             cls: 'btn',
12160                             cn: [
12161                                 {
12162                                     tag: 'span',
12163                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12164                                 }
12165                             ]
12166                         }
12167                     ]
12168                 },
12169                 {
12170                     tag: 'td',
12171                     cls: 'separator'
12172                 },
12173                 {
12174                     tag: 'td',
12175                     cn: [
12176                         {
12177                             tag: 'a',
12178                             href: '#',
12179                             cls: 'btn',
12180                             cn: [
12181                                 {
12182                                     tag: 'span',
12183                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12184                                 }
12185                             ]
12186                         }
12187                     ]
12188                 },
12189                 {
12190                     tag: 'td',
12191                     cls: 'separator'
12192                 }
12193             ]
12194         });
12195         
12196     },
12197     
12198     update: function()
12199     {
12200         
12201         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12202         
12203         this.fill();
12204     },
12205     
12206     fill: function() 
12207     {
12208         var hours = this.time.getHours();
12209         var minutes = this.time.getMinutes();
12210         var period = 'AM';
12211         
12212         if(hours > 11){
12213             period = 'PM';
12214         }
12215         
12216         if(hours == 0){
12217             hours = 12;
12218         }
12219         
12220         
12221         if(hours > 12){
12222             hours = hours - 12;
12223         }
12224         
12225         if(hours < 10){
12226             hours = '0' + hours;
12227         }
12228         
12229         if(minutes < 10){
12230             minutes = '0' + minutes;
12231         }
12232         
12233         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12234         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12235         this.pop.select('button', true).first().dom.innerHTML = period;
12236         
12237     },
12238     
12239     place: function()
12240     {   
12241         this.picker().removeClass(['bottom', 'top']);
12242         
12243         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12244             /*
12245              * place to the top of element!
12246              *
12247              */
12248             
12249             this.picker().addClass('top');
12250             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12251             
12252             return;
12253         }
12254         
12255         this.picker().addClass('bottom');
12256         
12257         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12258     },
12259   
12260     onFocus : function()
12261     {
12262         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12263         this.show();
12264     },
12265     
12266     onBlur : function()
12267     {
12268         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12269         this.hide();
12270     },
12271     
12272     show : function()
12273     {
12274         this.picker().show();
12275         this.pop.show();
12276         this.update();
12277         this.place();
12278         
12279         this.fireEvent('show', this, this.date);
12280     },
12281     
12282     hide : function()
12283     {
12284         this.picker().hide();
12285         this.pop.hide();
12286         
12287         this.fireEvent('hide', this, this.date);
12288     },
12289     
12290     setTime : function()
12291     {
12292         this.hide();
12293         this.setValue(this.time.format(this.format));
12294         
12295         this.fireEvent('select', this, this.date);
12296         
12297         
12298     },
12299     
12300     onMousedown: function(e){
12301         e.stopPropagation();
12302         e.preventDefault();
12303     },
12304     
12305     onIncrementHours: function()
12306     {
12307         Roo.log('onIncrementHours');
12308         this.time = this.time.add(Date.HOUR, 1);
12309         this.update();
12310         
12311     },
12312     
12313     onDecrementHours: function()
12314     {
12315         Roo.log('onDecrementHours');
12316         this.time = this.time.add(Date.HOUR, -1);
12317         this.update();
12318     },
12319     
12320     onIncrementMinutes: function()
12321     {
12322         Roo.log('onIncrementMinutes');
12323         this.time = this.time.add(Date.MINUTE, 1);
12324         this.update();
12325     },
12326     
12327     onDecrementMinutes: function()
12328     {
12329         Roo.log('onDecrementMinutes');
12330         this.time = this.time.add(Date.MINUTE, -1);
12331         this.update();
12332     },
12333     
12334     onTogglePeriod: function()
12335     {
12336         Roo.log('onTogglePeriod');
12337         this.time = this.time.add(Date.HOUR, 12);
12338         this.update();
12339     }
12340     
12341    
12342 });
12343
12344 Roo.apply(Roo.bootstrap.TimeField,  {
12345     
12346     content : {
12347         tag: 'tbody',
12348         cn: [
12349             {
12350                 tag: 'tr',
12351                 cn: [
12352                 {
12353                     tag: 'td',
12354                     colspan: '7'
12355                 }
12356                 ]
12357             }
12358         ]
12359     },
12360     
12361     footer : {
12362         tag: 'tfoot',
12363         cn: [
12364             {
12365                 tag: 'tr',
12366                 cn: [
12367                 {
12368                     tag: 'th',
12369                     colspan: '7',
12370                     cls: '',
12371                     cn: [
12372                         {
12373                             tag: 'button',
12374                             cls: 'btn btn-info ok',
12375                             html: 'OK'
12376                         }
12377                     ]
12378                 }
12379
12380                 ]
12381             }
12382         ]
12383     }
12384 });
12385
12386 Roo.apply(Roo.bootstrap.TimeField,  {
12387   
12388     template : {
12389         tag: 'div',
12390         cls: 'datepicker dropdown-menu',
12391         cn: [
12392             {
12393                 tag: 'div',
12394                 cls: 'datepicker-time',
12395                 cn: [
12396                 {
12397                     tag: 'table',
12398                     cls: 'table-condensed',
12399                     cn:[
12400                     Roo.bootstrap.TimeField.content,
12401                     Roo.bootstrap.TimeField.footer
12402                     ]
12403                 }
12404                 ]
12405             }
12406         ]
12407     }
12408 });
12409
12410  
12411
12412  /*
12413  * - LGPL
12414  *
12415  * CheckBox
12416  * 
12417  */
12418
12419 /**
12420  * @class Roo.bootstrap.CheckBox
12421  * @extends Roo.bootstrap.Input
12422  * Bootstrap CheckBox class
12423  * 
12424  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12425  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12426  * @cfg {String} boxLabel The text that appears beside the checkbox
12427  * @cfg {Boolean} checked initnal the element
12428  * 
12429  * @constructor
12430  * Create a new CheckBox
12431  * @param {Object} config The config object
12432  */
12433
12434 Roo.bootstrap.CheckBox = function(config){
12435     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12436    
12437         this.addEvents({
12438             /**
12439             * @event check
12440             * Fires when the element is checked or unchecked.
12441             * @param {Roo.bootstrap.CheckBox} this This input
12442             * @param {Boolean} checked The new checked value
12443             */
12444            check : true
12445         });
12446 };
12447
12448 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12449     
12450     inputType: 'checkbox',
12451     inputValue: 1,
12452     valueOff: 0,
12453     boxLabel: false,
12454     checked: false,
12455     
12456     getAutoCreate : function()
12457     {
12458         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12459         
12460         var id = Roo.id();
12461         
12462         var cfg = {};
12463         
12464         cfg.cls = 'form-group' //input-group
12465         
12466         var input =  {
12467             tag: 'input',
12468             id : id,
12469             type : this.inputType,
12470             value : (!this.checked) ? this.valueOff : this.inputValue,
12471             cls : 'form-box',
12472             placeholder : this.placeholder || ''
12473             
12474         };
12475         
12476         if (this.disabled) {
12477             input.disabled=true;
12478         }
12479         
12480         if(this.checked){
12481             input.checked = this.checked;
12482         }
12483         
12484         if (this.name) {
12485             input.name = this.name;
12486         }
12487         
12488         if (this.size) {
12489             input.cls += ' input-' + this.size;
12490         }
12491         
12492         var settings=this;
12493         ['xs','sm','md','lg'].map(function(size){
12494             if (settings[size]) {
12495                 cfg.cls += ' col-' + size + '-' + settings[size];
12496             }
12497         });
12498         
12499         var inputblock = input;
12500         
12501         if (this.before || this.after) {
12502             
12503             inputblock = {
12504                 cls : 'input-group',
12505                 cn :  [] 
12506             };
12507             if (this.before) {
12508                 inputblock.cn.push({
12509                     tag :'span',
12510                     cls : 'input-group-addon',
12511                     html : this.before
12512                 });
12513             }
12514             inputblock.cn.push(input);
12515             if (this.after) {
12516                 inputblock.cn.push({
12517                     tag :'span',
12518                     cls : 'input-group-addon',
12519                     html : this.after
12520                 });
12521             }
12522             
12523         };
12524         
12525         if (align ==='left' && this.fieldLabel.length) {
12526                 Roo.log("left and has label");
12527                 cfg.cn = [
12528                     
12529                     {
12530                         tag: 'label',
12531                         'for' :  id,
12532                         cls : 'control-label col-md-' + this.labelWidth,
12533                         html : this.fieldLabel
12534                         
12535                     },
12536                     {
12537                         cls : "col-md-" + (12 - this.labelWidth), 
12538                         cn: [
12539                             inputblock
12540                         ]
12541                     }
12542                     
12543                 ];
12544         } else if ( this.fieldLabel.length) {
12545                 Roo.log(" label");
12546                 cfg.cn = [
12547                    
12548                     {
12549                         tag: this.boxLabel ? 'span' : 'label',
12550                         'for': id,
12551                         cls: 'control-label box-input-label',
12552                         //cls : 'input-group-addon',
12553                         html : this.fieldLabel
12554                         
12555                     },
12556                     
12557                     inputblock
12558                     
12559                 ];
12560
12561         } else {
12562             
12563                    Roo.log(" no label && no align");
12564                 cfg.cn = [
12565                     
12566                         inputblock
12567                     
12568                 ];
12569                 
12570                 
12571         };
12572         
12573         if(this.boxLabel){
12574             cfg.cn.push({
12575                 tag: 'label',
12576                 'for': id,
12577                 cls: 'box-label',
12578                 html: this.boxLabel
12579             })
12580         }
12581         
12582         return cfg;
12583         
12584     },
12585     
12586     /**
12587      * return the real input element.
12588      */
12589     inputEl: function ()
12590     {
12591         return this.el.select('input.form-box',true).first();
12592     },
12593     
12594     initEvents : function()
12595     {
12596 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12597         
12598         this.inputEl().on('click', this.onClick,  this);
12599         
12600     },
12601     
12602     onClick : function()
12603     {   
12604         this.setChecked(!this.checked);
12605     },
12606     
12607     setChecked : function(state,suppressEvent)
12608     {
12609         this.checked = state;
12610         
12611         this.inputEl().dom.checked = state;
12612         
12613         if(suppressEvent !== true){
12614             this.fireEvent('check', this, state);
12615         }
12616         
12617         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12618         
12619     },
12620     
12621     setValue : function(v,suppressEvent)
12622     {
12623         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12624     }
12625     
12626 });
12627
12628  
12629 /*
12630  * - LGPL
12631  *
12632  * Radio
12633  * 
12634  */
12635
12636 /**
12637  * @class Roo.bootstrap.Radio
12638  * @extends Roo.bootstrap.CheckBox
12639  * Bootstrap Radio class
12640
12641  * @constructor
12642  * Create a new Radio
12643  * @param {Object} config The config object
12644  */
12645
12646 Roo.bootstrap.Radio = function(config){
12647     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12648    
12649 };
12650
12651 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12652     
12653     inputType: 'radio',
12654     inputValue: '',
12655     valueOff: '',
12656     
12657     getAutoCreate : function()
12658     {
12659         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12660         
12661         var id = Roo.id();
12662         
12663         var cfg = {};
12664         
12665         cfg.cls = 'form-group' //input-group
12666         
12667         var input =  {
12668             tag: 'input',
12669             id : id,
12670             type : this.inputType,
12671             value : (!this.checked) ? this.valueOff : this.inputValue,
12672             cls : 'form-box',
12673             placeholder : this.placeholder || ''
12674             
12675         };
12676         
12677         if (this.disabled) {
12678             input.disabled=true;
12679         }
12680         
12681         if(this.checked){
12682             input.checked = this.checked;
12683         }
12684         
12685         if (this.name) {
12686             input.name = this.name;
12687         }
12688         
12689         if (this.size) {
12690             input.cls += ' input-' + this.size;
12691         }
12692         
12693         var settings=this;
12694         ['xs','sm','md','lg'].map(function(size){
12695             if (settings[size]) {
12696                 cfg.cls += ' col-' + size + '-' + settings[size];
12697             }
12698         });
12699         
12700         var inputblock = input;
12701         
12702         if (this.before || this.after) {
12703             
12704             inputblock = {
12705                 cls : 'input-group',
12706                 cn :  [] 
12707             };
12708             if (this.before) {
12709                 inputblock.cn.push({
12710                     tag :'span',
12711                     cls : 'input-group-addon',
12712                     html : this.before
12713                 });
12714             }
12715             inputblock.cn.push(input);
12716             if (this.after) {
12717                 inputblock.cn.push({
12718                     tag :'span',
12719                     cls : 'input-group-addon',
12720                     html : this.after
12721                 });
12722             }
12723             
12724         };
12725         
12726         if (align ==='left' && this.fieldLabel.length) {
12727                 Roo.log("left and has label");
12728                 cfg.cn = [
12729                     
12730                     {
12731                         tag: 'label',
12732                         'for' :  id,
12733                         cls : 'control-label col-md-' + this.labelWidth,
12734                         html : this.fieldLabel
12735                         
12736                     },
12737                     {
12738                         cls : "col-md-" + (12 - this.labelWidth), 
12739                         cn: [
12740                             inputblock
12741                         ]
12742                     }
12743                     
12744                 ];
12745         } else if ( this.fieldLabel.length) {
12746                 Roo.log(" label");
12747                  cfg.cn = [
12748                    
12749                     {
12750                         tag: 'label',
12751                         'for': id,
12752                         cls: 'control-label box-input-label',
12753                         //cls : 'input-group-addon',
12754                         html : this.fieldLabel
12755                         
12756                     },
12757                     
12758                     inputblock
12759                     
12760                 ];
12761
12762         } else {
12763             
12764                    Roo.log(" no label && no align");
12765                 cfg.cn = [
12766                     
12767                         inputblock
12768                     
12769                 ];
12770                 
12771                 
12772         };
12773         
12774         if(this.boxLabel){
12775             cfg.cn.push({
12776                 tag: 'label',
12777                 'for': id,
12778                 cls: 'box-label',
12779                 html: this.boxLabel
12780             })
12781         }
12782         
12783         return cfg;
12784         
12785     },
12786    
12787     onClick : function()
12788     {   
12789         this.setChecked(true);
12790     },
12791     
12792     setChecked : function(state,suppressEvent)
12793     {
12794         if(state){
12795             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12796                 v.dom.checked = false;
12797             });
12798         }
12799         
12800         this.checked = state;
12801         this.inputEl().dom.checked = state;
12802         
12803         if(suppressEvent !== true){
12804             this.fireEvent('check', this, state);
12805         }
12806         
12807         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12808         
12809     },
12810     
12811     getGroupValue : function()
12812     {
12813         var value = ''
12814         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12815             if(v.dom.checked == true){
12816                 value = v.dom.value;
12817             }
12818         });
12819         
12820         return value;
12821     },
12822     
12823     /**
12824      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12825      * @return {Mixed} value The field value
12826      */
12827     getValue : function(){
12828         return this.getGroupValue();
12829     }
12830     
12831 });
12832
12833  
12834 //<script type="text/javascript">
12835
12836 /*
12837  * Based  Ext JS Library 1.1.1
12838  * Copyright(c) 2006-2007, Ext JS, LLC.
12839  * LGPL
12840  *
12841  */
12842  
12843 /**
12844  * @class Roo.HtmlEditorCore
12845  * @extends Roo.Component
12846  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12847  *
12848  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12849  */
12850
12851 Roo.HtmlEditorCore = function(config){
12852     
12853     
12854     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12855     this.addEvents({
12856         /**
12857          * @event initialize
12858          * Fires when the editor is fully initialized (including the iframe)
12859          * @param {Roo.HtmlEditorCore} this
12860          */
12861         initialize: true,
12862         /**
12863          * @event activate
12864          * Fires when the editor is first receives the focus. Any insertion must wait
12865          * until after this event.
12866          * @param {Roo.HtmlEditorCore} this
12867          */
12868         activate: true,
12869          /**
12870          * @event beforesync
12871          * Fires before the textarea is updated with content from the editor iframe. Return false
12872          * to cancel the sync.
12873          * @param {Roo.HtmlEditorCore} this
12874          * @param {String} html
12875          */
12876         beforesync: true,
12877          /**
12878          * @event beforepush
12879          * Fires before the iframe editor is updated with content from the textarea. Return false
12880          * to cancel the push.
12881          * @param {Roo.HtmlEditorCore} this
12882          * @param {String} html
12883          */
12884         beforepush: true,
12885          /**
12886          * @event sync
12887          * Fires when the textarea is updated with content from the editor iframe.
12888          * @param {Roo.HtmlEditorCore} this
12889          * @param {String} html
12890          */
12891         sync: true,
12892          /**
12893          * @event push
12894          * Fires when the iframe editor is updated with content from the textarea.
12895          * @param {Roo.HtmlEditorCore} this
12896          * @param {String} html
12897          */
12898         push: true,
12899         
12900         /**
12901          * @event editorevent
12902          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12903          * @param {Roo.HtmlEditorCore} this
12904          */
12905         editorevent: true
12906     });
12907      
12908 };
12909
12910
12911 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12912
12913
12914      /**
12915      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12916      */
12917     
12918     owner : false,
12919     
12920      /**
12921      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12922      *                        Roo.resizable.
12923      */
12924     resizable : false,
12925      /**
12926      * @cfg {Number} height (in pixels)
12927      */   
12928     height: 300,
12929    /**
12930      * @cfg {Number} width (in pixels)
12931      */   
12932     width: 500,
12933     
12934     /**
12935      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12936      * 
12937      */
12938     stylesheets: false,
12939     
12940     // id of frame..
12941     frameId: false,
12942     
12943     // private properties
12944     validationEvent : false,
12945     deferHeight: true,
12946     initialized : false,
12947     activated : false,
12948     sourceEditMode : false,
12949     onFocus : Roo.emptyFn,
12950     iframePad:3,
12951     hideMode:'offsets',
12952     
12953     clearUp: true,
12954     
12955      
12956     
12957
12958     /**
12959      * Protected method that will not generally be called directly. It
12960      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12961      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12962      */
12963     getDocMarkup : function(){
12964         // body styles..
12965         var st = '';
12966         Roo.log(this.stylesheets);
12967         
12968         // inherit styels from page...?? 
12969         if (this.stylesheets === false) {
12970             
12971             Roo.get(document.head).select('style').each(function(node) {
12972                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12973             });
12974             
12975             Roo.get(document.head).select('link').each(function(node) { 
12976                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12977             });
12978             
12979         } else if (!this.stylesheets.length) {
12980                 // simple..
12981                 st = '<style type="text/css">' +
12982                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12983                    '</style>';
12984         } else {
12985             Roo.each(this.stylesheets, function(s) {
12986                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12987             });
12988             
12989         }
12990         
12991         st +=  '<style type="text/css">' +
12992             'IMG { cursor: pointer } ' +
12993         '</style>';
12994
12995         
12996         return '<html><head>' + st  +
12997             //<style type="text/css">' +
12998             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12999             //'</style>' +
13000             ' </head><body class="roo-htmleditor-body"></body></html>';
13001     },
13002
13003     // private
13004     onRender : function(ct, position)
13005     {
13006         var _t = this;
13007         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13008         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13009         
13010         
13011         this.el.dom.style.border = '0 none';
13012         this.el.dom.setAttribute('tabIndex', -1);
13013         this.el.addClass('x-hidden hide');
13014         
13015         
13016         
13017         if(Roo.isIE){ // fix IE 1px bogus margin
13018             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13019         }
13020        
13021         
13022         this.frameId = Roo.id();
13023         
13024          
13025         
13026         var iframe = this.owner.wrap.createChild({
13027             tag: 'iframe',
13028             cls: 'form-control', // bootstrap..
13029             id: this.frameId,
13030             name: this.frameId,
13031             frameBorder : 'no',
13032             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13033         }, this.el
13034         );
13035         
13036         
13037         this.iframe = iframe.dom;
13038
13039          this.assignDocWin();
13040         
13041         this.doc.designMode = 'on';
13042        
13043         this.doc.open();
13044         this.doc.write(this.getDocMarkup());
13045         this.doc.close();
13046
13047         
13048         var task = { // must defer to wait for browser to be ready
13049             run : function(){
13050                 //console.log("run task?" + this.doc.readyState);
13051                 this.assignDocWin();
13052                 if(this.doc.body || this.doc.readyState == 'complete'){
13053                     try {
13054                         this.doc.designMode="on";
13055                     } catch (e) {
13056                         return;
13057                     }
13058                     Roo.TaskMgr.stop(task);
13059                     this.initEditor.defer(10, this);
13060                 }
13061             },
13062             interval : 10,
13063             duration: 10000,
13064             scope: this
13065         };
13066         Roo.TaskMgr.start(task);
13067
13068         
13069          
13070     },
13071
13072     // private
13073     onResize : function(w, h)
13074     {
13075          Roo.log('resize: ' +w + ',' + h );
13076         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13077         if(!this.iframe){
13078             return;
13079         }
13080         if(typeof w == 'number'){
13081             
13082             this.iframe.style.width = w + 'px';
13083         }
13084         if(typeof h == 'number'){
13085             
13086             this.iframe.style.height = h + 'px';
13087             if(this.doc){
13088                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13089             }
13090         }
13091         
13092     },
13093
13094     /**
13095      * Toggles the editor between standard and source edit mode.
13096      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13097      */
13098     toggleSourceEdit : function(sourceEditMode){
13099         
13100         this.sourceEditMode = sourceEditMode === true;
13101         
13102         if(this.sourceEditMode){
13103  
13104             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13105             
13106         }else{
13107             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13108             //this.iframe.className = '';
13109             this.deferFocus();
13110         }
13111         //this.setSize(this.owner.wrap.getSize());
13112         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13113     },
13114
13115     
13116   
13117
13118     /**
13119      * Protected method that will not generally be called directly. If you need/want
13120      * custom HTML cleanup, this is the method you should override.
13121      * @param {String} html The HTML to be cleaned
13122      * return {String} The cleaned HTML
13123      */
13124     cleanHtml : function(html){
13125         html = String(html);
13126         if(html.length > 5){
13127             if(Roo.isSafari){ // strip safari nonsense
13128                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13129             }
13130         }
13131         if(html == '&nbsp;'){
13132             html = '';
13133         }
13134         return html;
13135     },
13136
13137     /**
13138      * HTML Editor -> Textarea
13139      * Protected method that will not generally be called directly. Syncs the contents
13140      * of the editor iframe with the textarea.
13141      */
13142     syncValue : function(){
13143         if(this.initialized){
13144             var bd = (this.doc.body || this.doc.documentElement);
13145             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13146             var html = bd.innerHTML;
13147             if(Roo.isSafari){
13148                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13149                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13150                 if(m && m[1]){
13151                     html = '<div style="'+m[0]+'">' + html + '</div>';
13152                 }
13153             }
13154             html = this.cleanHtml(html);
13155             // fix up the special chars.. normaly like back quotes in word...
13156             // however we do not want to do this with chinese..
13157             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13158                 var cc = b.charCodeAt();
13159                 if (
13160                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13161                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13162                     (cc >= 0xf900 && cc < 0xfb00 )
13163                 ) {
13164                         return b;
13165                 }
13166                 return "&#"+cc+";" 
13167             });
13168             if(this.owner.fireEvent('beforesync', this, html) !== false){
13169                 this.el.dom.value = html;
13170                 this.owner.fireEvent('sync', this, html);
13171             }
13172         }
13173     },
13174
13175     /**
13176      * Protected method that will not generally be called directly. Pushes the value of the textarea
13177      * into the iframe editor.
13178      */
13179     pushValue : function(){
13180         if(this.initialized){
13181             var v = this.el.dom.value.trim();
13182             
13183 //            if(v.length < 1){
13184 //                v = '&#160;';
13185 //            }
13186             
13187             if(this.owner.fireEvent('beforepush', this, v) !== false){
13188                 var d = (this.doc.body || this.doc.documentElement);
13189                 d.innerHTML = v;
13190                 this.cleanUpPaste();
13191                 this.el.dom.value = d.innerHTML;
13192                 this.owner.fireEvent('push', this, v);
13193             }
13194         }
13195     },
13196
13197     // private
13198     deferFocus : function(){
13199         this.focus.defer(10, this);
13200     },
13201
13202     // doc'ed in Field
13203     focus : function(){
13204         if(this.win && !this.sourceEditMode){
13205             this.win.focus();
13206         }else{
13207             this.el.focus();
13208         }
13209     },
13210     
13211     assignDocWin: function()
13212     {
13213         var iframe = this.iframe;
13214         
13215          if(Roo.isIE){
13216             this.doc = iframe.contentWindow.document;
13217             this.win = iframe.contentWindow;
13218         } else {
13219             if (!Roo.get(this.frameId)) {
13220                 return;
13221             }
13222             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13223             this.win = Roo.get(this.frameId).dom.contentWindow;
13224         }
13225     },
13226     
13227     // private
13228     initEditor : function(){
13229         //console.log("INIT EDITOR");
13230         this.assignDocWin();
13231         
13232         
13233         
13234         this.doc.designMode="on";
13235         this.doc.open();
13236         this.doc.write(this.getDocMarkup());
13237         this.doc.close();
13238         
13239         var dbody = (this.doc.body || this.doc.documentElement);
13240         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13241         // this copies styles from the containing element into thsi one..
13242         // not sure why we need all of this..
13243         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13244         ss['background-attachment'] = 'fixed'; // w3c
13245         dbody.bgProperties = 'fixed'; // ie
13246         Roo.DomHelper.applyStyles(dbody, ss);
13247         Roo.EventManager.on(this.doc, {
13248             //'mousedown': this.onEditorEvent,
13249             'mouseup': this.onEditorEvent,
13250             'dblclick': this.onEditorEvent,
13251             'click': this.onEditorEvent,
13252             'keyup': this.onEditorEvent,
13253             buffer:100,
13254             scope: this
13255         });
13256         if(Roo.isGecko){
13257             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13258         }
13259         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13260             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13261         }
13262         this.initialized = true;
13263
13264         this.owner.fireEvent('initialize', this);
13265         this.pushValue();
13266     },
13267
13268     // private
13269     onDestroy : function(){
13270         
13271         
13272         
13273         if(this.rendered){
13274             
13275             //for (var i =0; i < this.toolbars.length;i++) {
13276             //    // fixme - ask toolbars for heights?
13277             //    this.toolbars[i].onDestroy();
13278            // }
13279             
13280             //this.wrap.dom.innerHTML = '';
13281             //this.wrap.remove();
13282         }
13283     },
13284
13285     // private
13286     onFirstFocus : function(){
13287         
13288         this.assignDocWin();
13289         
13290         
13291         this.activated = true;
13292          
13293     
13294         if(Roo.isGecko){ // prevent silly gecko errors
13295             this.win.focus();
13296             var s = this.win.getSelection();
13297             if(!s.focusNode || s.focusNode.nodeType != 3){
13298                 var r = s.getRangeAt(0);
13299                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13300                 r.collapse(true);
13301                 this.deferFocus();
13302             }
13303             try{
13304                 this.execCmd('useCSS', true);
13305                 this.execCmd('styleWithCSS', false);
13306             }catch(e){}
13307         }
13308         this.owner.fireEvent('activate', this);
13309     },
13310
13311     // private
13312     adjustFont: function(btn){
13313         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13314         //if(Roo.isSafari){ // safari
13315         //    adjust *= 2;
13316        // }
13317         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13318         if(Roo.isSafari){ // safari
13319             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13320             v =  (v < 10) ? 10 : v;
13321             v =  (v > 48) ? 48 : v;
13322             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13323             
13324         }
13325         
13326         
13327         v = Math.max(1, v+adjust);
13328         
13329         this.execCmd('FontSize', v  );
13330     },
13331
13332     onEditorEvent : function(e){
13333         this.owner.fireEvent('editorevent', this, e);
13334       //  this.updateToolbar();
13335         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13336     },
13337
13338     insertTag : function(tg)
13339     {
13340         // could be a bit smarter... -> wrap the current selected tRoo..
13341         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13342             
13343             range = this.createRange(this.getSelection());
13344             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13345             wrappingNode.appendChild(range.extractContents());
13346             range.insertNode(wrappingNode);
13347
13348             return;
13349             
13350             
13351             
13352         }
13353         this.execCmd("formatblock",   tg);
13354         
13355     },
13356     
13357     insertText : function(txt)
13358     {
13359         
13360         
13361         var range = this.createRange();
13362         range.deleteContents();
13363                //alert(Sender.getAttribute('label'));
13364                
13365         range.insertNode(this.doc.createTextNode(txt));
13366     } ,
13367     
13368      
13369
13370     /**
13371      * Executes a Midas editor command on the editor document and performs necessary focus and
13372      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13373      * @param {String} cmd The Midas command
13374      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13375      */
13376     relayCmd : function(cmd, value){
13377         this.win.focus();
13378         this.execCmd(cmd, value);
13379         this.owner.fireEvent('editorevent', this);
13380         //this.updateToolbar();
13381         this.owner.deferFocus();
13382     },
13383
13384     /**
13385      * Executes a Midas editor command directly on the editor document.
13386      * For visual commands, you should use {@link #relayCmd} instead.
13387      * <b>This should only be called after the editor is initialized.</b>
13388      * @param {String} cmd The Midas command
13389      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13390      */
13391     execCmd : function(cmd, value){
13392         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13393         this.syncValue();
13394     },
13395  
13396  
13397    
13398     /**
13399      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13400      * to insert tRoo.
13401      * @param {String} text | dom node.. 
13402      */
13403     insertAtCursor : function(text)
13404     {
13405         
13406         
13407         
13408         if(!this.activated){
13409             return;
13410         }
13411         /*
13412         if(Roo.isIE){
13413             this.win.focus();
13414             var r = this.doc.selection.createRange();
13415             if(r){
13416                 r.collapse(true);
13417                 r.pasteHTML(text);
13418                 this.syncValue();
13419                 this.deferFocus();
13420             
13421             }
13422             return;
13423         }
13424         */
13425         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13426             this.win.focus();
13427             
13428             
13429             // from jquery ui (MIT licenced)
13430             var range, node;
13431             var win = this.win;
13432             
13433             if (win.getSelection && win.getSelection().getRangeAt) {
13434                 range = win.getSelection().getRangeAt(0);
13435                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13436                 range.insertNode(node);
13437             } else if (win.document.selection && win.document.selection.createRange) {
13438                 // no firefox support
13439                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13440                 win.document.selection.createRange().pasteHTML(txt);
13441             } else {
13442                 // no firefox support
13443                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13444                 this.execCmd('InsertHTML', txt);
13445             } 
13446             
13447             this.syncValue();
13448             
13449             this.deferFocus();
13450         }
13451     },
13452  // private
13453     mozKeyPress : function(e){
13454         if(e.ctrlKey){
13455             var c = e.getCharCode(), cmd;
13456           
13457             if(c > 0){
13458                 c = String.fromCharCode(c).toLowerCase();
13459                 switch(c){
13460                     case 'b':
13461                         cmd = 'bold';
13462                         break;
13463                     case 'i':
13464                         cmd = 'italic';
13465                         break;
13466                     
13467                     case 'u':
13468                         cmd = 'underline';
13469                         break;
13470                     
13471                     case 'v':
13472                         this.cleanUpPaste.defer(100, this);
13473                         return;
13474                         
13475                 }
13476                 if(cmd){
13477                     this.win.focus();
13478                     this.execCmd(cmd);
13479                     this.deferFocus();
13480                     e.preventDefault();
13481                 }
13482                 
13483             }
13484         }
13485     },
13486
13487     // private
13488     fixKeys : function(){ // load time branching for fastest keydown performance
13489         if(Roo.isIE){
13490             return function(e){
13491                 var k = e.getKey(), r;
13492                 if(k == e.TAB){
13493                     e.stopEvent();
13494                     r = this.doc.selection.createRange();
13495                     if(r){
13496                         r.collapse(true);
13497                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13498                         this.deferFocus();
13499                     }
13500                     return;
13501                 }
13502                 
13503                 if(k == e.ENTER){
13504                     r = this.doc.selection.createRange();
13505                     if(r){
13506                         var target = r.parentElement();
13507                         if(!target || target.tagName.toLowerCase() != 'li'){
13508                             e.stopEvent();
13509                             r.pasteHTML('<br />');
13510                             r.collapse(false);
13511                             r.select();
13512                         }
13513                     }
13514                 }
13515                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13516                     this.cleanUpPaste.defer(100, this);
13517                     return;
13518                 }
13519                 
13520                 
13521             };
13522         }else if(Roo.isOpera){
13523             return function(e){
13524                 var k = e.getKey();
13525                 if(k == e.TAB){
13526                     e.stopEvent();
13527                     this.win.focus();
13528                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13529                     this.deferFocus();
13530                 }
13531                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13532                     this.cleanUpPaste.defer(100, this);
13533                     return;
13534                 }
13535                 
13536             };
13537         }else if(Roo.isSafari){
13538             return function(e){
13539                 var k = e.getKey();
13540                 
13541                 if(k == e.TAB){
13542                     e.stopEvent();
13543                     this.execCmd('InsertText','\t');
13544                     this.deferFocus();
13545                     return;
13546                 }
13547                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13548                     this.cleanUpPaste.defer(100, this);
13549                     return;
13550                 }
13551                 
13552              };
13553         }
13554     }(),
13555     
13556     getAllAncestors: function()
13557     {
13558         var p = this.getSelectedNode();
13559         var a = [];
13560         if (!p) {
13561             a.push(p); // push blank onto stack..
13562             p = this.getParentElement();
13563         }
13564         
13565         
13566         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13567             a.push(p);
13568             p = p.parentNode;
13569         }
13570         a.push(this.doc.body);
13571         return a;
13572     },
13573     lastSel : false,
13574     lastSelNode : false,
13575     
13576     
13577     getSelection : function() 
13578     {
13579         this.assignDocWin();
13580         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13581     },
13582     
13583     getSelectedNode: function() 
13584     {
13585         // this may only work on Gecko!!!
13586         
13587         // should we cache this!!!!
13588         
13589         
13590         
13591          
13592         var range = this.createRange(this.getSelection()).cloneRange();
13593         
13594         if (Roo.isIE) {
13595             var parent = range.parentElement();
13596             while (true) {
13597                 var testRange = range.duplicate();
13598                 testRange.moveToElementText(parent);
13599                 if (testRange.inRange(range)) {
13600                     break;
13601                 }
13602                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13603                     break;
13604                 }
13605                 parent = parent.parentElement;
13606             }
13607             return parent;
13608         }
13609         
13610         // is ancestor a text element.
13611         var ac =  range.commonAncestorContainer;
13612         if (ac.nodeType == 3) {
13613             ac = ac.parentNode;
13614         }
13615         
13616         var ar = ac.childNodes;
13617          
13618         var nodes = [];
13619         var other_nodes = [];
13620         var has_other_nodes = false;
13621         for (var i=0;i<ar.length;i++) {
13622             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13623                 continue;
13624             }
13625             // fullly contained node.
13626             
13627             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13628                 nodes.push(ar[i]);
13629                 continue;
13630             }
13631             
13632             // probably selected..
13633             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13634                 other_nodes.push(ar[i]);
13635                 continue;
13636             }
13637             // outer..
13638             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13639                 continue;
13640             }
13641             
13642             
13643             has_other_nodes = true;
13644         }
13645         if (!nodes.length && other_nodes.length) {
13646             nodes= other_nodes;
13647         }
13648         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13649             return false;
13650         }
13651         
13652         return nodes[0];
13653     },
13654     createRange: function(sel)
13655     {
13656         // this has strange effects when using with 
13657         // top toolbar - not sure if it's a great idea.
13658         //this.editor.contentWindow.focus();
13659         if (typeof sel != "undefined") {
13660             try {
13661                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13662             } catch(e) {
13663                 return this.doc.createRange();
13664             }
13665         } else {
13666             return this.doc.createRange();
13667         }
13668     },
13669     getParentElement: function()
13670     {
13671         
13672         this.assignDocWin();
13673         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13674         
13675         var range = this.createRange(sel);
13676          
13677         try {
13678             var p = range.commonAncestorContainer;
13679             while (p.nodeType == 3) { // text node
13680                 p = p.parentNode;
13681             }
13682             return p;
13683         } catch (e) {
13684             return null;
13685         }
13686     
13687     },
13688     /***
13689      *
13690      * Range intersection.. the hard stuff...
13691      *  '-1' = before
13692      *  '0' = hits..
13693      *  '1' = after.
13694      *         [ -- selected range --- ]
13695      *   [fail]                        [fail]
13696      *
13697      *    basically..
13698      *      if end is before start or  hits it. fail.
13699      *      if start is after end or hits it fail.
13700      *
13701      *   if either hits (but other is outside. - then it's not 
13702      *   
13703      *    
13704      **/
13705     
13706     
13707     // @see http://www.thismuchiknow.co.uk/?p=64.
13708     rangeIntersectsNode : function(range, node)
13709     {
13710         var nodeRange = node.ownerDocument.createRange();
13711         try {
13712             nodeRange.selectNode(node);
13713         } catch (e) {
13714             nodeRange.selectNodeContents(node);
13715         }
13716     
13717         var rangeStartRange = range.cloneRange();
13718         rangeStartRange.collapse(true);
13719     
13720         var rangeEndRange = range.cloneRange();
13721         rangeEndRange.collapse(false);
13722     
13723         var nodeStartRange = nodeRange.cloneRange();
13724         nodeStartRange.collapse(true);
13725     
13726         var nodeEndRange = nodeRange.cloneRange();
13727         nodeEndRange.collapse(false);
13728     
13729         return rangeStartRange.compareBoundaryPoints(
13730                  Range.START_TO_START, nodeEndRange) == -1 &&
13731                rangeEndRange.compareBoundaryPoints(
13732                  Range.START_TO_START, nodeStartRange) == 1;
13733         
13734          
13735     },
13736     rangeCompareNode : function(range, node)
13737     {
13738         var nodeRange = node.ownerDocument.createRange();
13739         try {
13740             nodeRange.selectNode(node);
13741         } catch (e) {
13742             nodeRange.selectNodeContents(node);
13743         }
13744         
13745         
13746         range.collapse(true);
13747     
13748         nodeRange.collapse(true);
13749      
13750         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13751         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13752          
13753         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13754         
13755         var nodeIsBefore   =  ss == 1;
13756         var nodeIsAfter    = ee == -1;
13757         
13758         if (nodeIsBefore && nodeIsAfter)
13759             return 0; // outer
13760         if (!nodeIsBefore && nodeIsAfter)
13761             return 1; //right trailed.
13762         
13763         if (nodeIsBefore && !nodeIsAfter)
13764             return 2;  // left trailed.
13765         // fully contined.
13766         return 3;
13767     },
13768
13769     // private? - in a new class?
13770     cleanUpPaste :  function()
13771     {
13772         // cleans up the whole document..
13773         Roo.log('cleanuppaste');
13774         
13775         this.cleanUpChildren(this.doc.body);
13776         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13777         if (clean != this.doc.body.innerHTML) {
13778             this.doc.body.innerHTML = clean;
13779         }
13780         
13781     },
13782     
13783     cleanWordChars : function(input) {// change the chars to hex code
13784         var he = Roo.HtmlEditorCore;
13785         
13786         var output = input;
13787         Roo.each(he.swapCodes, function(sw) { 
13788             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13789             
13790             output = output.replace(swapper, sw[1]);
13791         });
13792         
13793         return output;
13794     },
13795     
13796     
13797     cleanUpChildren : function (n)
13798     {
13799         if (!n.childNodes.length) {
13800             return;
13801         }
13802         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13803            this.cleanUpChild(n.childNodes[i]);
13804         }
13805     },
13806     
13807     
13808         
13809     
13810     cleanUpChild : function (node)
13811     {
13812         var ed = this;
13813         //console.log(node);
13814         if (node.nodeName == "#text") {
13815             // clean up silly Windows -- stuff?
13816             return; 
13817         }
13818         if (node.nodeName == "#comment") {
13819             node.parentNode.removeChild(node);
13820             // clean up silly Windows -- stuff?
13821             return; 
13822         }
13823         
13824         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13825             // remove node.
13826             node.parentNode.removeChild(node);
13827             return;
13828             
13829         }
13830         
13831         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13832         
13833         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13834         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13835         
13836         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13837         //    remove_keep_children = true;
13838         //}
13839         
13840         if (remove_keep_children) {
13841             this.cleanUpChildren(node);
13842             // inserts everything just before this node...
13843             while (node.childNodes.length) {
13844                 var cn = node.childNodes[0];
13845                 node.removeChild(cn);
13846                 node.parentNode.insertBefore(cn, node);
13847             }
13848             node.parentNode.removeChild(node);
13849             return;
13850         }
13851         
13852         if (!node.attributes || !node.attributes.length) {
13853             this.cleanUpChildren(node);
13854             return;
13855         }
13856         
13857         function cleanAttr(n,v)
13858         {
13859             
13860             if (v.match(/^\./) || v.match(/^\//)) {
13861                 return;
13862             }
13863             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13864                 return;
13865             }
13866             if (v.match(/^#/)) {
13867                 return;
13868             }
13869 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13870             node.removeAttribute(n);
13871             
13872         }
13873         
13874         function cleanStyle(n,v)
13875         {
13876             if (v.match(/expression/)) { //XSS?? should we even bother..
13877                 node.removeAttribute(n);
13878                 return;
13879             }
13880             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13881             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13882             
13883             
13884             var parts = v.split(/;/);
13885             var clean = [];
13886             
13887             Roo.each(parts, function(p) {
13888                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13889                 if (!p.length) {
13890                     return true;
13891                 }
13892                 var l = p.split(':').shift().replace(/\s+/g,'');
13893                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13894                 
13895                 if ( cblack.indexOf(l) > -1) {
13896 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13897                     //node.removeAttribute(n);
13898                     return true;
13899                 }
13900                 //Roo.log()
13901                 // only allow 'c whitelisted system attributes'
13902                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13903 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13904                     //node.removeAttribute(n);
13905                     return true;
13906                 }
13907                 
13908                 
13909                  
13910                 
13911                 clean.push(p);
13912                 return true;
13913             });
13914             if (clean.length) { 
13915                 node.setAttribute(n, clean.join(';'));
13916             } else {
13917                 node.removeAttribute(n);
13918             }
13919             
13920         }
13921         
13922         
13923         for (var i = node.attributes.length-1; i > -1 ; i--) {
13924             var a = node.attributes[i];
13925             //console.log(a);
13926             
13927             if (a.name.toLowerCase().substr(0,2)=='on')  {
13928                 node.removeAttribute(a.name);
13929                 continue;
13930             }
13931             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13932                 node.removeAttribute(a.name);
13933                 continue;
13934             }
13935             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13936                 cleanAttr(a.name,a.value); // fixme..
13937                 continue;
13938             }
13939             if (a.name == 'style') {
13940                 cleanStyle(a.name,a.value);
13941                 continue;
13942             }
13943             /// clean up MS crap..
13944             // tecnically this should be a list of valid class'es..
13945             
13946             
13947             if (a.name == 'class') {
13948                 if (a.value.match(/^Mso/)) {
13949                     node.className = '';
13950                 }
13951                 
13952                 if (a.value.match(/body/)) {
13953                     node.className = '';
13954                 }
13955                 continue;
13956             }
13957             
13958             // style cleanup!?
13959             // class cleanup?
13960             
13961         }
13962         
13963         
13964         this.cleanUpChildren(node);
13965         
13966         
13967     }
13968     
13969     
13970     // hide stuff that is not compatible
13971     /**
13972      * @event blur
13973      * @hide
13974      */
13975     /**
13976      * @event change
13977      * @hide
13978      */
13979     /**
13980      * @event focus
13981      * @hide
13982      */
13983     /**
13984      * @event specialkey
13985      * @hide
13986      */
13987     /**
13988      * @cfg {String} fieldClass @hide
13989      */
13990     /**
13991      * @cfg {String} focusClass @hide
13992      */
13993     /**
13994      * @cfg {String} autoCreate @hide
13995      */
13996     /**
13997      * @cfg {String} inputType @hide
13998      */
13999     /**
14000      * @cfg {String} invalidClass @hide
14001      */
14002     /**
14003      * @cfg {String} invalidText @hide
14004      */
14005     /**
14006      * @cfg {String} msgFx @hide
14007      */
14008     /**
14009      * @cfg {String} validateOnBlur @hide
14010      */
14011 });
14012
14013 Roo.HtmlEditorCore.white = [
14014         'area', 'br', 'img', 'input', 'hr', 'wbr',
14015         
14016        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14017        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14018        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14019        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14020        'table',   'ul',         'xmp', 
14021        
14022        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14023       'thead',   'tr', 
14024      
14025       'dir', 'menu', 'ol', 'ul', 'dl',
14026        
14027       'embed',  'object'
14028 ];
14029
14030
14031 Roo.HtmlEditorCore.black = [
14032     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14033         'applet', // 
14034         'base',   'basefont', 'bgsound', 'blink',  'body', 
14035         'frame',  'frameset', 'head',    'html',   'ilayer', 
14036         'iframe', 'layer',  'link',     'meta',    'object',   
14037         'script', 'style' ,'title',  'xml' // clean later..
14038 ];
14039 Roo.HtmlEditorCore.clean = [
14040     'script', 'style', 'title', 'xml'
14041 ];
14042 Roo.HtmlEditorCore.remove = [
14043     'font'
14044 ];
14045 // attributes..
14046
14047 Roo.HtmlEditorCore.ablack = [
14048     'on'
14049 ];
14050     
14051 Roo.HtmlEditorCore.aclean = [ 
14052     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14053 ];
14054
14055 // protocols..
14056 Roo.HtmlEditorCore.pwhite= [
14057         'http',  'https',  'mailto'
14058 ];
14059
14060 // white listed style attributes.
14061 Roo.HtmlEditorCore.cwhite= [
14062       //  'text-align', /// default is to allow most things..
14063       
14064          
14065 //        'font-size'//??
14066 ];
14067
14068 // black listed style attributes.
14069 Roo.HtmlEditorCore.cblack= [
14070       //  'font-size' -- this can be set by the project 
14071 ];
14072
14073
14074 Roo.HtmlEditorCore.swapCodes   =[ 
14075     [    8211, "--" ], 
14076     [    8212, "--" ], 
14077     [    8216,  "'" ],  
14078     [    8217, "'" ],  
14079     [    8220, '"' ],  
14080     [    8221, '"' ],  
14081     [    8226, "*" ],  
14082     [    8230, "..." ]
14083 ]; 
14084
14085     /*
14086  * - LGPL
14087  *
14088  * HtmlEditor
14089  * 
14090  */
14091
14092 /**
14093  * @class Roo.bootstrap.HtmlEditor
14094  * @extends Roo.bootstrap.TextArea
14095  * Bootstrap HtmlEditor class
14096
14097  * @constructor
14098  * Create a new HtmlEditor
14099  * @param {Object} config The config object
14100  */
14101
14102 Roo.bootstrap.HtmlEditor = function(config){
14103     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14104     if (!this.toolbars) {
14105         this.toolbars = [];
14106     }
14107     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14108     this.addEvents({
14109             /**
14110              * @event initialize
14111              * Fires when the editor is fully initialized (including the iframe)
14112              * @param {HtmlEditor} this
14113              */
14114             initialize: true,
14115             /**
14116              * @event activate
14117              * Fires when the editor is first receives the focus. Any insertion must wait
14118              * until after this event.
14119              * @param {HtmlEditor} this
14120              */
14121             activate: true,
14122              /**
14123              * @event beforesync
14124              * Fires before the textarea is updated with content from the editor iframe. Return false
14125              * to cancel the sync.
14126              * @param {HtmlEditor} this
14127              * @param {String} html
14128              */
14129             beforesync: true,
14130              /**
14131              * @event beforepush
14132              * Fires before the iframe editor is updated with content from the textarea. Return false
14133              * to cancel the push.
14134              * @param {HtmlEditor} this
14135              * @param {String} html
14136              */
14137             beforepush: true,
14138              /**
14139              * @event sync
14140              * Fires when the textarea is updated with content from the editor iframe.
14141              * @param {HtmlEditor} this
14142              * @param {String} html
14143              */
14144             sync: true,
14145              /**
14146              * @event push
14147              * Fires when the iframe editor is updated with content from the textarea.
14148              * @param {HtmlEditor} this
14149              * @param {String} html
14150              */
14151             push: true,
14152              /**
14153              * @event editmodechange
14154              * Fires when the editor switches edit modes
14155              * @param {HtmlEditor} this
14156              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14157              */
14158             editmodechange: true,
14159             /**
14160              * @event editorevent
14161              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14162              * @param {HtmlEditor} this
14163              */
14164             editorevent: true,
14165             /**
14166              * @event firstfocus
14167              * Fires when on first focus - needed by toolbars..
14168              * @param {HtmlEditor} this
14169              */
14170             firstfocus: true,
14171             /**
14172              * @event autosave
14173              * Auto save the htmlEditor value as a file into Events
14174              * @param {HtmlEditor} this
14175              */
14176             autosave: true,
14177             /**
14178              * @event savedpreview
14179              * preview the saved version of htmlEditor
14180              * @param {HtmlEditor} this
14181              */
14182             savedpreview: true
14183         });
14184 };
14185
14186
14187 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14188     
14189     
14190       /**
14191      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14192      */
14193     toolbars : false,
14194    
14195      /**
14196      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14197      *                        Roo.resizable.
14198      */
14199     resizable : false,
14200      /**
14201      * @cfg {Number} height (in pixels)
14202      */   
14203     height: 300,
14204    /**
14205      * @cfg {Number} width (in pixels)
14206      */   
14207     width: false,
14208     
14209     /**
14210      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14211      * 
14212      */
14213     stylesheets: false,
14214     
14215     // id of frame..
14216     frameId: false,
14217     
14218     // private properties
14219     validationEvent : false,
14220     deferHeight: true,
14221     initialized : false,
14222     activated : false,
14223     
14224     onFocus : Roo.emptyFn,
14225     iframePad:3,
14226     hideMode:'offsets',
14227     
14228     
14229     tbContainer : false,
14230     
14231     toolbarContainer :function() {
14232         return this.wrap.select('.x-html-editor-tb',true).first();
14233     },
14234
14235     /**
14236      * Protected method that will not generally be called directly. It
14237      * is called when the editor creates its toolbar. Override this method if you need to
14238      * add custom toolbar buttons.
14239      * @param {HtmlEditor} editor
14240      */
14241     createToolbar : function(){
14242         
14243         Roo.log("create toolbars");
14244         
14245         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14246         this.toolbars[0].render(this.toolbarContainer());
14247         
14248         return;
14249         
14250 //        if (!editor.toolbars || !editor.toolbars.length) {
14251 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14252 //        }
14253 //        
14254 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14255 //            editor.toolbars[i] = Roo.factory(
14256 //                    typeof(editor.toolbars[i]) == 'string' ?
14257 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14258 //                Roo.bootstrap.HtmlEditor);
14259 //            editor.toolbars[i].init(editor);
14260 //        }
14261     },
14262
14263      
14264     // private
14265     onRender : function(ct, position)
14266     {
14267        // Roo.log("Call onRender: " + this.xtype);
14268         var _t = this;
14269         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14270       
14271         this.wrap = this.inputEl().wrap({
14272             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14273         });
14274         
14275         this.editorcore.onRender(ct, position);
14276          
14277         if (this.resizable) {
14278             this.resizeEl = new Roo.Resizable(this.wrap, {
14279                 pinned : true,
14280                 wrap: true,
14281                 dynamic : true,
14282                 minHeight : this.height,
14283                 height: this.height,
14284                 handles : this.resizable,
14285                 width: this.width,
14286                 listeners : {
14287                     resize : function(r, w, h) {
14288                         _t.onResize(w,h); // -something
14289                     }
14290                 }
14291             });
14292             
14293         }
14294         this.createToolbar(this);
14295        
14296         
14297         if(!this.width && this.resizable){
14298             this.setSize(this.wrap.getSize());
14299         }
14300         if (this.resizeEl) {
14301             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14302             // should trigger onReize..
14303         }
14304         
14305     },
14306
14307     // private
14308     onResize : function(w, h)
14309     {
14310         Roo.log('resize: ' +w + ',' + h );
14311         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14312         var ew = false;
14313         var eh = false;
14314         
14315         if(this.inputEl() ){
14316             if(typeof w == 'number'){
14317                 var aw = w - this.wrap.getFrameWidth('lr');
14318                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14319                 ew = aw;
14320             }
14321             if(typeof h == 'number'){
14322                  var tbh = -11;  // fixme it needs to tool bar size!
14323                 for (var i =0; i < this.toolbars.length;i++) {
14324                     // fixme - ask toolbars for heights?
14325                     tbh += this.toolbars[i].el.getHeight();
14326                     //if (this.toolbars[i].footer) {
14327                     //    tbh += this.toolbars[i].footer.el.getHeight();
14328                     //}
14329                 }
14330               
14331                 
14332                 
14333                 
14334                 
14335                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14336                 ah -= 5; // knock a few pixes off for look..
14337                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14338                 var eh = ah;
14339             }
14340         }
14341         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14342         this.editorcore.onResize(ew,eh);
14343         
14344     },
14345
14346     /**
14347      * Toggles the editor between standard and source edit mode.
14348      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14349      */
14350     toggleSourceEdit : function(sourceEditMode)
14351     {
14352         this.editorcore.toggleSourceEdit(sourceEditMode);
14353         
14354         if(this.editorcore.sourceEditMode){
14355             Roo.log('editor - showing textarea');
14356             
14357 //            Roo.log('in');
14358 //            Roo.log(this.syncValue());
14359             this.syncValue();
14360             this.inputEl().removeClass('hide');
14361             this.inputEl().dom.removeAttribute('tabIndex');
14362             this.inputEl().focus();
14363         }else{
14364             Roo.log('editor - hiding textarea');
14365 //            Roo.log('out')
14366 //            Roo.log(this.pushValue()); 
14367             this.pushValue();
14368             
14369             this.inputEl().addClass('hide');
14370             this.inputEl().dom.setAttribute('tabIndex', -1);
14371             //this.deferFocus();
14372         }
14373          
14374         if(this.resizable){
14375             this.setSize(this.wrap.getSize());
14376         }
14377         
14378         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14379     },
14380  
14381     // private (for BoxComponent)
14382     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14383
14384     // private (for BoxComponent)
14385     getResizeEl : function(){
14386         return this.wrap;
14387     },
14388
14389     // private (for BoxComponent)
14390     getPositionEl : function(){
14391         return this.wrap;
14392     },
14393
14394     // private
14395     initEvents : function(){
14396         this.originalValue = this.getValue();
14397     },
14398
14399 //    /**
14400 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14401 //     * @method
14402 //     */
14403 //    markInvalid : Roo.emptyFn,
14404 //    /**
14405 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14406 //     * @method
14407 //     */
14408 //    clearInvalid : Roo.emptyFn,
14409
14410     setValue : function(v){
14411         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14412         this.editorcore.pushValue();
14413     },
14414
14415      
14416     // private
14417     deferFocus : function(){
14418         this.focus.defer(10, this);
14419     },
14420
14421     // doc'ed in Field
14422     focus : function(){
14423         this.editorcore.focus();
14424         
14425     },
14426       
14427
14428     // private
14429     onDestroy : function(){
14430         
14431         
14432         
14433         if(this.rendered){
14434             
14435             for (var i =0; i < this.toolbars.length;i++) {
14436                 // fixme - ask toolbars for heights?
14437                 this.toolbars[i].onDestroy();
14438             }
14439             
14440             this.wrap.dom.innerHTML = '';
14441             this.wrap.remove();
14442         }
14443     },
14444
14445     // private
14446     onFirstFocus : function(){
14447         //Roo.log("onFirstFocus");
14448         this.editorcore.onFirstFocus();
14449          for (var i =0; i < this.toolbars.length;i++) {
14450             this.toolbars[i].onFirstFocus();
14451         }
14452         
14453     },
14454     
14455     // private
14456     syncValue : function()
14457     {   
14458         this.editorcore.syncValue();
14459     },
14460     
14461     pushValue : function()
14462     {   
14463         this.editorcore.pushValue();
14464     }
14465      
14466     
14467     // hide stuff that is not compatible
14468     /**
14469      * @event blur
14470      * @hide
14471      */
14472     /**
14473      * @event change
14474      * @hide
14475      */
14476     /**
14477      * @event focus
14478      * @hide
14479      */
14480     /**
14481      * @event specialkey
14482      * @hide
14483      */
14484     /**
14485      * @cfg {String} fieldClass @hide
14486      */
14487     /**
14488      * @cfg {String} focusClass @hide
14489      */
14490     /**
14491      * @cfg {String} autoCreate @hide
14492      */
14493     /**
14494      * @cfg {String} inputType @hide
14495      */
14496     /**
14497      * @cfg {String} invalidClass @hide
14498      */
14499     /**
14500      * @cfg {String} invalidText @hide
14501      */
14502     /**
14503      * @cfg {String} msgFx @hide
14504      */
14505     /**
14506      * @cfg {String} validateOnBlur @hide
14507      */
14508 });
14509  
14510     
14511    
14512    
14513    
14514       
14515
14516 /**
14517  * @class Roo.bootstrap.HtmlEditorToolbar1
14518  * Basic Toolbar
14519  * 
14520  * Usage:
14521  *
14522  new Roo.bootstrap.HtmlEditor({
14523     ....
14524     toolbars : [
14525         new Roo.bootstrap.HtmlEditorToolbar1({
14526             disable : { fonts: 1 , format: 1, ..., ... , ...],
14527             btns : [ .... ]
14528         })
14529     }
14530      
14531  * 
14532  * @cfg {Object} disable List of elements to disable..
14533  * @cfg {Array} btns List of additional buttons.
14534  * 
14535  * 
14536  * NEEDS Extra CSS? 
14537  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14538  */
14539  
14540 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14541 {
14542     
14543     Roo.apply(this, config);
14544     
14545     // default disabled, based on 'good practice'..
14546     this.disable = this.disable || {};
14547     Roo.applyIf(this.disable, {
14548         fontSize : true,
14549         colors : true,
14550         specialElements : true
14551     });
14552     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14553     
14554     this.editor = config.editor;
14555     this.editorcore = config.editor.editorcore;
14556     
14557     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14558     
14559     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14560     // dont call parent... till later.
14561 }
14562 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14563     
14564     
14565     bar : true,
14566     
14567     editor : false,
14568     editorcore : false,
14569     
14570     
14571     formats : [
14572         "p" ,  
14573         "h1","h2","h3","h4","h5","h6", 
14574         "pre", "code", 
14575         "abbr", "acronym", "address", "cite", "samp", "var",
14576         'div','span'
14577     ],
14578     
14579     onRender : function(ct, position)
14580     {
14581        // Roo.log("Call onRender: " + this.xtype);
14582         
14583        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14584        Roo.log(this.el);
14585        this.el.dom.style.marginBottom = '0';
14586        var _this = this;
14587        var editorcore = this.editorcore;
14588        var editor= this.editor;
14589        
14590        var children = [];
14591        var btn = function(id,cmd , toggle, handler){
14592        
14593             var  event = toggle ? 'toggle' : 'click';
14594        
14595             var a = {
14596                 size : 'sm',
14597                 xtype: 'Button',
14598                 xns: Roo.bootstrap,
14599                 glyphicon : id,
14600                 cmd : id || cmd,
14601                 enableToggle:toggle !== false,
14602                 //html : 'submit'
14603                 pressed : toggle ? false : null,
14604                 listeners : {}
14605             }
14606             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14607                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14608             }
14609             children.push(a);
14610             return a;
14611        }
14612         
14613         var style = {
14614                 xtype: 'Button',
14615                 size : 'sm',
14616                 xns: Roo.bootstrap,
14617                 glyphicon : 'font',
14618                 //html : 'submit'
14619                 menu : {
14620                     xtype: 'Menu',
14621                     xns: Roo.bootstrap,
14622                     items:  []
14623                 }
14624         };
14625         Roo.each(this.formats, function(f) {
14626             style.menu.items.push({
14627                 xtype :'MenuItem',
14628                 xns: Roo.bootstrap,
14629                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14630                 tagname : f,
14631                 listeners : {
14632                     click : function()
14633                     {
14634                         editorcore.insertTag(this.tagname);
14635                         editor.focus();
14636                     }
14637                 }
14638                 
14639             });
14640         });
14641          children.push(style);   
14642             
14643             
14644         btn('bold',false,true);
14645         btn('italic',false,true);
14646         btn('align-left', 'justifyleft',true);
14647         btn('align-center', 'justifycenter',true);
14648         btn('align-right' , 'justifyright',true);
14649         btn('link', false, false, function(btn) {
14650             //Roo.log("create link?");
14651             var url = prompt(this.createLinkText, this.defaultLinkValue);
14652             if(url && url != 'http:/'+'/'){
14653                 this.editorcore.relayCmd('createlink', url);
14654             }
14655         }),
14656         btn('list','insertunorderedlist',true);
14657         btn('pencil', false,true, function(btn){
14658                 Roo.log(this);
14659                 
14660                 this.toggleSourceEdit(btn.pressed);
14661         });
14662         /*
14663         var cog = {
14664                 xtype: 'Button',
14665                 size : 'sm',
14666                 xns: Roo.bootstrap,
14667                 glyphicon : 'cog',
14668                 //html : 'submit'
14669                 menu : {
14670                     xtype: 'Menu',
14671                     xns: Roo.bootstrap,
14672                     items:  []
14673                 }
14674         };
14675         
14676         cog.menu.items.push({
14677             xtype :'MenuItem',
14678             xns: Roo.bootstrap,
14679             html : Clean styles,
14680             tagname : f,
14681             listeners : {
14682                 click : function()
14683                 {
14684                     editorcore.insertTag(this.tagname);
14685                     editor.focus();
14686                 }
14687             }
14688             
14689         });
14690        */
14691         
14692          
14693        this.xtype = 'Navbar';
14694         
14695         for(var i=0;i< children.length;i++) {
14696             
14697             this.buttons.add(this.addxtypeChild(children[i]));
14698             
14699         }
14700         
14701         editor.on('editorevent', this.updateToolbar, this);
14702     },
14703     onBtnClick : function(id)
14704     {
14705        this.editorcore.relayCmd(id);
14706        this.editorcore.focus();
14707     },
14708     
14709     /**
14710      * Protected method that will not generally be called directly. It triggers
14711      * a toolbar update by reading the markup state of the current selection in the editor.
14712      */
14713     updateToolbar: function(){
14714
14715         if(!this.editorcore.activated){
14716             this.editor.onFirstFocus(); // is this neeed?
14717             return;
14718         }
14719
14720         var btns = this.buttons; 
14721         var doc = this.editorcore.doc;
14722         btns.get('bold').setActive(doc.queryCommandState('bold'));
14723         btns.get('italic').setActive(doc.queryCommandState('italic'));
14724         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14725         
14726         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14727         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14728         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14729         
14730         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14731         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14732          /*
14733         
14734         var ans = this.editorcore.getAllAncestors();
14735         if (this.formatCombo) {
14736             
14737             
14738             var store = this.formatCombo.store;
14739             this.formatCombo.setValue("");
14740             for (var i =0; i < ans.length;i++) {
14741                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14742                     // select it..
14743                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14744                     break;
14745                 }
14746             }
14747         }
14748         
14749         
14750         
14751         // hides menus... - so this cant be on a menu...
14752         Roo.bootstrap.MenuMgr.hideAll();
14753         */
14754         Roo.bootstrap.MenuMgr.hideAll();
14755         //this.editorsyncValue();
14756     },
14757     onFirstFocus: function() {
14758         this.buttons.each(function(item){
14759            item.enable();
14760         });
14761     },
14762     toggleSourceEdit : function(sourceEditMode){
14763         
14764           
14765         if(sourceEditMode){
14766             Roo.log("disabling buttons");
14767            this.buttons.each( function(item){
14768                 if(item.cmd != 'pencil'){
14769                     item.disable();
14770                 }
14771             });
14772           
14773         }else{
14774             Roo.log("enabling buttons");
14775             if(this.editorcore.initialized){
14776                 this.buttons.each( function(item){
14777                     item.enable();
14778                 });
14779             }
14780             
14781         }
14782         Roo.log("calling toggole on editor");
14783         // tell the editor that it's been pressed..
14784         this.editor.toggleSourceEdit(sourceEditMode);
14785        
14786     }
14787 });
14788
14789
14790
14791
14792
14793 /**
14794  * @class Roo.bootstrap.Table.AbstractSelectionModel
14795  * @extends Roo.util.Observable
14796  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14797  * implemented by descendant classes.  This class should not be directly instantiated.
14798  * @constructor
14799  */
14800 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14801     this.locked = false;
14802     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14803 };
14804
14805
14806 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14807     /** @ignore Called by the grid automatically. Do not call directly. */
14808     init : function(grid){
14809         this.grid = grid;
14810         this.initEvents();
14811     },
14812
14813     /**
14814      * Locks the selections.
14815      */
14816     lock : function(){
14817         this.locked = true;
14818     },
14819
14820     /**
14821      * Unlocks the selections.
14822      */
14823     unlock : function(){
14824         this.locked = false;
14825     },
14826
14827     /**
14828      * Returns true if the selections are locked.
14829      * @return {Boolean}
14830      */
14831     isLocked : function(){
14832         return this.locked;
14833     }
14834 });
14835 /**
14836  * @class Roo.bootstrap.Table.ColumnModel
14837  * @extends Roo.util.Observable
14838  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14839  * the columns in the table.
14840  
14841  * @constructor
14842  * @param {Object} config An Array of column config objects. See this class's
14843  * config objects for details.
14844 */
14845 Roo.bootstrap.Table.ColumnModel = function(config){
14846         /**
14847      * The config passed into the constructor
14848      */
14849     this.config = config;
14850     this.lookup = {};
14851
14852     // if no id, create one
14853     // if the column does not have a dataIndex mapping,
14854     // map it to the order it is in the config
14855     for(var i = 0, len = config.length; i < len; i++){
14856         var c = config[i];
14857         if(typeof c.dataIndex == "undefined"){
14858             c.dataIndex = i;
14859         }
14860         if(typeof c.renderer == "string"){
14861             c.renderer = Roo.util.Format[c.renderer];
14862         }
14863         if(typeof c.id == "undefined"){
14864             c.id = Roo.id();
14865         }
14866 //        if(c.editor && c.editor.xtype){
14867 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14868 //        }
14869 //        if(c.editor && c.editor.isFormField){
14870 //            c.editor = new Roo.grid.GridEditor(c.editor);
14871 //        }
14872
14873         this.lookup[c.id] = c;
14874     }
14875
14876     /**
14877      * The width of columns which have no width specified (defaults to 100)
14878      * @type Number
14879      */
14880     this.defaultWidth = 100;
14881
14882     /**
14883      * Default sortable of columns which have no sortable specified (defaults to false)
14884      * @type Boolean
14885      */
14886     this.defaultSortable = false;
14887
14888     this.addEvents({
14889         /**
14890              * @event widthchange
14891              * Fires when the width of a column changes.
14892              * @param {ColumnModel} this
14893              * @param {Number} columnIndex The column index
14894              * @param {Number} newWidth The new width
14895              */
14896             "widthchange": true,
14897         /**
14898              * @event headerchange
14899              * Fires when the text of a header changes.
14900              * @param {ColumnModel} this
14901              * @param {Number} columnIndex The column index
14902              * @param {Number} newText The new header text
14903              */
14904             "headerchange": true,
14905         /**
14906              * @event hiddenchange
14907              * Fires when a column is hidden or "unhidden".
14908              * @param {ColumnModel} this
14909              * @param {Number} columnIndex The column index
14910              * @param {Boolean} hidden true if hidden, false otherwise
14911              */
14912             "hiddenchange": true,
14913             /**
14914          * @event columnmoved
14915          * Fires when a column is moved.
14916          * @param {ColumnModel} this
14917          * @param {Number} oldIndex
14918          * @param {Number} newIndex
14919          */
14920         "columnmoved" : true,
14921         /**
14922          * @event columlockchange
14923          * Fires when a column's locked state is changed
14924          * @param {ColumnModel} this
14925          * @param {Number} colIndex
14926          * @param {Boolean} locked true if locked
14927          */
14928         "columnlockchange" : true
14929     });
14930     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14931 };
14932 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14933     /**
14934      * @cfg {String} header The header text to display in the Grid view.
14935      */
14936     /**
14937      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14938      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14939      * specified, the column's index is used as an index into the Record's data Array.
14940      */
14941     /**
14942      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14943      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14944      */
14945     /**
14946      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14947      * Defaults to the value of the {@link #defaultSortable} property.
14948      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14949      */
14950     /**
14951      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14952      */
14953     /**
14954      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14955      */
14956     /**
14957      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14958      */
14959     /**
14960      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14961      */
14962     /**
14963      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14964      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14965      * default renderer uses the raw data value.
14966      */
14967     /**
14968      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14969      */
14970
14971     /**
14972      * Returns the id of the column at the specified index.
14973      * @param {Number} index The column index
14974      * @return {String} the id
14975      */
14976     getColumnId : function(index){
14977         return this.config[index].id;
14978     },
14979
14980     /**
14981      * Returns the column for a specified id.
14982      * @param {String} id The column id
14983      * @return {Object} the column
14984      */
14985     getColumnById : function(id){
14986         return this.lookup[id];
14987     },
14988
14989     
14990     /**
14991      * Returns the column for a specified dataIndex.
14992      * @param {String} dataIndex The column dataIndex
14993      * @return {Object|Boolean} the column or false if not found
14994      */
14995     getColumnByDataIndex: function(dataIndex){
14996         var index = this.findColumnIndex(dataIndex);
14997         return index > -1 ? this.config[index] : false;
14998     },
14999     
15000     /**
15001      * Returns the index for a specified column id.
15002      * @param {String} id The column id
15003      * @return {Number} the index, or -1 if not found
15004      */
15005     getIndexById : function(id){
15006         for(var i = 0, len = this.config.length; i < len; i++){
15007             if(this.config[i].id == id){
15008                 return i;
15009             }
15010         }
15011         return -1;
15012     },
15013     
15014     /**
15015      * Returns the index for a specified column dataIndex.
15016      * @param {String} dataIndex The column dataIndex
15017      * @return {Number} the index, or -1 if not found
15018      */
15019     
15020     findColumnIndex : function(dataIndex){
15021         for(var i = 0, len = this.config.length; i < len; i++){
15022             if(this.config[i].dataIndex == dataIndex){
15023                 return i;
15024             }
15025         }
15026         return -1;
15027     },
15028     
15029     
15030     moveColumn : function(oldIndex, newIndex){
15031         var c = this.config[oldIndex];
15032         this.config.splice(oldIndex, 1);
15033         this.config.splice(newIndex, 0, c);
15034         this.dataMap = null;
15035         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15036     },
15037
15038     isLocked : function(colIndex){
15039         return this.config[colIndex].locked === true;
15040     },
15041
15042     setLocked : function(colIndex, value, suppressEvent){
15043         if(this.isLocked(colIndex) == value){
15044             return;
15045         }
15046         this.config[colIndex].locked = value;
15047         if(!suppressEvent){
15048             this.fireEvent("columnlockchange", this, colIndex, value);
15049         }
15050     },
15051
15052     getTotalLockedWidth : function(){
15053         var totalWidth = 0;
15054         for(var i = 0; i < this.config.length; i++){
15055             if(this.isLocked(i) && !this.isHidden(i)){
15056                 this.totalWidth += this.getColumnWidth(i);
15057             }
15058         }
15059         return totalWidth;
15060     },
15061
15062     getLockedCount : function(){
15063         for(var i = 0, len = this.config.length; i < len; i++){
15064             if(!this.isLocked(i)){
15065                 return i;
15066             }
15067         }
15068     },
15069
15070     /**
15071      * Returns the number of columns.
15072      * @return {Number}
15073      */
15074     getColumnCount : function(visibleOnly){
15075         if(visibleOnly === true){
15076             var c = 0;
15077             for(var i = 0, len = this.config.length; i < len; i++){
15078                 if(!this.isHidden(i)){
15079                     c++;
15080                 }
15081             }
15082             return c;
15083         }
15084         return this.config.length;
15085     },
15086
15087     /**
15088      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15089      * @param {Function} fn
15090      * @param {Object} scope (optional)
15091      * @return {Array} result
15092      */
15093     getColumnsBy : function(fn, scope){
15094         var r = [];
15095         for(var i = 0, len = this.config.length; i < len; i++){
15096             var c = this.config[i];
15097             if(fn.call(scope||this, c, i) === true){
15098                 r[r.length] = c;
15099             }
15100         }
15101         return r;
15102     },
15103
15104     /**
15105      * Returns true if the specified column is sortable.
15106      * @param {Number} col The column index
15107      * @return {Boolean}
15108      */
15109     isSortable : function(col){
15110         if(typeof this.config[col].sortable == "undefined"){
15111             return this.defaultSortable;
15112         }
15113         return this.config[col].sortable;
15114     },
15115
15116     /**
15117      * Returns the rendering (formatting) function defined for the column.
15118      * @param {Number} col The column index.
15119      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15120      */
15121     getRenderer : function(col){
15122         if(!this.config[col].renderer){
15123             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15124         }
15125         return this.config[col].renderer;
15126     },
15127
15128     /**
15129      * Sets the rendering (formatting) function for a column.
15130      * @param {Number} col The column index
15131      * @param {Function} fn The function to use to process the cell's raw data
15132      * to return HTML markup for the grid view. The render function is called with
15133      * the following parameters:<ul>
15134      * <li>Data value.</li>
15135      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15136      * <li>css A CSS style string to apply to the table cell.</li>
15137      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15138      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15139      * <li>Row index</li>
15140      * <li>Column index</li>
15141      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15142      */
15143     setRenderer : function(col, fn){
15144         this.config[col].renderer = fn;
15145     },
15146
15147     /**
15148      * Returns the width for the specified column.
15149      * @param {Number} col The column index
15150      * @return {Number}
15151      */
15152     getColumnWidth : function(col){
15153         return this.config[col].width * 1 || this.defaultWidth;
15154     },
15155
15156     /**
15157      * Sets the width for a column.
15158      * @param {Number} col The column index
15159      * @param {Number} width The new width
15160      */
15161     setColumnWidth : function(col, width, suppressEvent){
15162         this.config[col].width = width;
15163         this.totalWidth = null;
15164         if(!suppressEvent){
15165              this.fireEvent("widthchange", this, col, width);
15166         }
15167     },
15168
15169     /**
15170      * Returns the total width of all columns.
15171      * @param {Boolean} includeHidden True to include hidden column widths
15172      * @return {Number}
15173      */
15174     getTotalWidth : function(includeHidden){
15175         if(!this.totalWidth){
15176             this.totalWidth = 0;
15177             for(var i = 0, len = this.config.length; i < len; i++){
15178                 if(includeHidden || !this.isHidden(i)){
15179                     this.totalWidth += this.getColumnWidth(i);
15180                 }
15181             }
15182         }
15183         return this.totalWidth;
15184     },
15185
15186     /**
15187      * Returns the header for the specified column.
15188      * @param {Number} col The column index
15189      * @return {String}
15190      */
15191     getColumnHeader : function(col){
15192         return this.config[col].header;
15193     },
15194
15195     /**
15196      * Sets the header for a column.
15197      * @param {Number} col The column index
15198      * @param {String} header The new header
15199      */
15200     setColumnHeader : function(col, header){
15201         this.config[col].header = header;
15202         this.fireEvent("headerchange", this, col, header);
15203     },
15204
15205     /**
15206      * Returns the tooltip for the specified column.
15207      * @param {Number} col The column index
15208      * @return {String}
15209      */
15210     getColumnTooltip : function(col){
15211             return this.config[col].tooltip;
15212     },
15213     /**
15214      * Sets the tooltip for a column.
15215      * @param {Number} col The column index
15216      * @param {String} tooltip The new tooltip
15217      */
15218     setColumnTooltip : function(col, tooltip){
15219             this.config[col].tooltip = tooltip;
15220     },
15221
15222     /**
15223      * Returns the dataIndex for the specified column.
15224      * @param {Number} col The column index
15225      * @return {Number}
15226      */
15227     getDataIndex : function(col){
15228         return this.config[col].dataIndex;
15229     },
15230
15231     /**
15232      * Sets the dataIndex for a column.
15233      * @param {Number} col The column index
15234      * @param {Number} dataIndex The new dataIndex
15235      */
15236     setDataIndex : function(col, dataIndex){
15237         this.config[col].dataIndex = dataIndex;
15238     },
15239
15240     
15241     
15242     /**
15243      * Returns true if the cell is editable.
15244      * @param {Number} colIndex The column index
15245      * @param {Number} rowIndex The row index
15246      * @return {Boolean}
15247      */
15248     isCellEditable : function(colIndex, rowIndex){
15249         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15250     },
15251
15252     /**
15253      * Returns the editor defined for the cell/column.
15254      * return false or null to disable editing.
15255      * @param {Number} colIndex The column index
15256      * @param {Number} rowIndex The row index
15257      * @return {Object}
15258      */
15259     getCellEditor : function(colIndex, rowIndex){
15260         return this.config[colIndex].editor;
15261     },
15262
15263     /**
15264      * Sets if a column is editable.
15265      * @param {Number} col The column index
15266      * @param {Boolean} editable True if the column is editable
15267      */
15268     setEditable : function(col, editable){
15269         this.config[col].editable = editable;
15270     },
15271
15272
15273     /**
15274      * Returns true if the column is hidden.
15275      * @param {Number} colIndex The column index
15276      * @return {Boolean}
15277      */
15278     isHidden : function(colIndex){
15279         return this.config[colIndex].hidden;
15280     },
15281
15282
15283     /**
15284      * Returns true if the column width cannot be changed
15285      */
15286     isFixed : function(colIndex){
15287         return this.config[colIndex].fixed;
15288     },
15289
15290     /**
15291      * Returns true if the column can be resized
15292      * @return {Boolean}
15293      */
15294     isResizable : function(colIndex){
15295         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15296     },
15297     /**
15298      * Sets if a column is hidden.
15299      * @param {Number} colIndex The column index
15300      * @param {Boolean} hidden True if the column is hidden
15301      */
15302     setHidden : function(colIndex, hidden){
15303         this.config[colIndex].hidden = hidden;
15304         this.totalWidth = null;
15305         this.fireEvent("hiddenchange", this, colIndex, hidden);
15306     },
15307
15308     /**
15309      * Sets the editor for a column.
15310      * @param {Number} col The column index
15311      * @param {Object} editor The editor object
15312      */
15313     setEditor : function(col, editor){
15314         this.config[col].editor = editor;
15315     }
15316 });
15317
15318 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15319         if(typeof value == "string" && value.length < 1){
15320             return "&#160;";
15321         }
15322         return value;
15323 };
15324
15325 // Alias for backwards compatibility
15326 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15327
15328 /**
15329  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15330  * @class Roo.bootstrap.Table.RowSelectionModel
15331  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15332  * It supports multiple selections and keyboard selection/navigation. 
15333  * @constructor
15334  * @param {Object} config
15335  */
15336
15337 Roo.bootstrap.Table.RowSelectionModel = function(config){
15338     Roo.apply(this, config);
15339     this.selections = new Roo.util.MixedCollection(false, function(o){
15340         return o.id;
15341     });
15342
15343     this.last = false;
15344     this.lastActive = false;
15345
15346     this.addEvents({
15347         /**
15348              * @event selectionchange
15349              * Fires when the selection changes
15350              * @param {SelectionModel} this
15351              */
15352             "selectionchange" : true,
15353         /**
15354              * @event afterselectionchange
15355              * Fires after the selection changes (eg. by key press or clicking)
15356              * @param {SelectionModel} this
15357              */
15358             "afterselectionchange" : true,
15359         /**
15360              * @event beforerowselect
15361              * Fires when a row is selected being selected, return false to cancel.
15362              * @param {SelectionModel} this
15363              * @param {Number} rowIndex The selected index
15364              * @param {Boolean} keepExisting False if other selections will be cleared
15365              */
15366             "beforerowselect" : true,
15367         /**
15368              * @event rowselect
15369              * Fires when a row is selected.
15370              * @param {SelectionModel} this
15371              * @param {Number} rowIndex The selected index
15372              * @param {Roo.data.Record} r The record
15373              */
15374             "rowselect" : true,
15375         /**
15376              * @event rowdeselect
15377              * Fires when a row is deselected.
15378              * @param {SelectionModel} this
15379              * @param {Number} rowIndex The selected index
15380              */
15381         "rowdeselect" : true
15382     });
15383     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15384     this.locked = false;
15385 };
15386
15387 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15388     /**
15389      * @cfg {Boolean} singleSelect
15390      * True to allow selection of only one row at a time (defaults to false)
15391      */
15392     singleSelect : false,
15393
15394     // private
15395     initEvents : function(){
15396
15397         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15398             this.grid.on("mousedown", this.handleMouseDown, this);
15399         }else{ // allow click to work like normal
15400             this.grid.on("rowclick", this.handleDragableRowClick, this);
15401         }
15402
15403         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15404             "up" : function(e){
15405                 if(!e.shiftKey){
15406                     this.selectPrevious(e.shiftKey);
15407                 }else if(this.last !== false && this.lastActive !== false){
15408                     var last = this.last;
15409                     this.selectRange(this.last,  this.lastActive-1);
15410                     this.grid.getView().focusRow(this.lastActive);
15411                     if(last !== false){
15412                         this.last = last;
15413                     }
15414                 }else{
15415                     this.selectFirstRow();
15416                 }
15417                 this.fireEvent("afterselectionchange", this);
15418             },
15419             "down" : function(e){
15420                 if(!e.shiftKey){
15421                     this.selectNext(e.shiftKey);
15422                 }else if(this.last !== false && this.lastActive !== false){
15423                     var last = this.last;
15424                     this.selectRange(this.last,  this.lastActive+1);
15425                     this.grid.getView().focusRow(this.lastActive);
15426                     if(last !== false){
15427                         this.last = last;
15428                     }
15429                 }else{
15430                     this.selectFirstRow();
15431                 }
15432                 this.fireEvent("afterselectionchange", this);
15433             },
15434             scope: this
15435         });
15436
15437         var view = this.grid.view;
15438         view.on("refresh", this.onRefresh, this);
15439         view.on("rowupdated", this.onRowUpdated, this);
15440         view.on("rowremoved", this.onRemove, this);
15441     },
15442
15443     // private
15444     onRefresh : function(){
15445         var ds = this.grid.dataSource, i, v = this.grid.view;
15446         var s = this.selections;
15447         s.each(function(r){
15448             if((i = ds.indexOfId(r.id)) != -1){
15449                 v.onRowSelect(i);
15450             }else{
15451                 s.remove(r);
15452             }
15453         });
15454     },
15455
15456     // private
15457     onRemove : function(v, index, r){
15458         this.selections.remove(r);
15459     },
15460
15461     // private
15462     onRowUpdated : function(v, index, r){
15463         if(this.isSelected(r)){
15464             v.onRowSelect(index);
15465         }
15466     },
15467
15468     /**
15469      * Select records.
15470      * @param {Array} records The records to select
15471      * @param {Boolean} keepExisting (optional) True to keep existing selections
15472      */
15473     selectRecords : function(records, keepExisting){
15474         if(!keepExisting){
15475             this.clearSelections();
15476         }
15477         var ds = this.grid.dataSource;
15478         for(var i = 0, len = records.length; i < len; i++){
15479             this.selectRow(ds.indexOf(records[i]), true);
15480         }
15481     },
15482
15483     /**
15484      * Gets the number of selected rows.
15485      * @return {Number}
15486      */
15487     getCount : function(){
15488         return this.selections.length;
15489     },
15490
15491     /**
15492      * Selects the first row in the grid.
15493      */
15494     selectFirstRow : function(){
15495         this.selectRow(0);
15496     },
15497
15498     /**
15499      * Select the last row.
15500      * @param {Boolean} keepExisting (optional) True to keep existing selections
15501      */
15502     selectLastRow : function(keepExisting){
15503         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15504     },
15505
15506     /**
15507      * Selects the row immediately following the last selected row.
15508      * @param {Boolean} keepExisting (optional) True to keep existing selections
15509      */
15510     selectNext : function(keepExisting){
15511         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15512             this.selectRow(this.last+1, keepExisting);
15513             this.grid.getView().focusRow(this.last);
15514         }
15515     },
15516
15517     /**
15518      * Selects the row that precedes the last selected row.
15519      * @param {Boolean} keepExisting (optional) True to keep existing selections
15520      */
15521     selectPrevious : function(keepExisting){
15522         if(this.last){
15523             this.selectRow(this.last-1, keepExisting);
15524             this.grid.getView().focusRow(this.last);
15525         }
15526     },
15527
15528     /**
15529      * Returns the selected records
15530      * @return {Array} Array of selected records
15531      */
15532     getSelections : function(){
15533         return [].concat(this.selections.items);
15534     },
15535
15536     /**
15537      * Returns the first selected record.
15538      * @return {Record}
15539      */
15540     getSelected : function(){
15541         return this.selections.itemAt(0);
15542     },
15543
15544
15545     /**
15546      * Clears all selections.
15547      */
15548     clearSelections : function(fast){
15549         if(this.locked) return;
15550         if(fast !== true){
15551             var ds = this.grid.dataSource;
15552             var s = this.selections;
15553             s.each(function(r){
15554                 this.deselectRow(ds.indexOfId(r.id));
15555             }, this);
15556             s.clear();
15557         }else{
15558             this.selections.clear();
15559         }
15560         this.last = false;
15561     },
15562
15563
15564     /**
15565      * Selects all rows.
15566      */
15567     selectAll : function(){
15568         if(this.locked) return;
15569         this.selections.clear();
15570         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15571             this.selectRow(i, true);
15572         }
15573     },
15574
15575     /**
15576      * Returns True if there is a selection.
15577      * @return {Boolean}
15578      */
15579     hasSelection : function(){
15580         return this.selections.length > 0;
15581     },
15582
15583     /**
15584      * Returns True if the specified row is selected.
15585      * @param {Number/Record} record The record or index of the record to check
15586      * @return {Boolean}
15587      */
15588     isSelected : function(index){
15589         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15590         return (r && this.selections.key(r.id) ? true : false);
15591     },
15592
15593     /**
15594      * Returns True if the specified record id is selected.
15595      * @param {String} id The id of record to check
15596      * @return {Boolean}
15597      */
15598     isIdSelected : function(id){
15599         return (this.selections.key(id) ? true : false);
15600     },
15601
15602     // private
15603     handleMouseDown : function(e, t){
15604         var view = this.grid.getView(), rowIndex;
15605         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15606             return;
15607         };
15608         if(e.shiftKey && this.last !== false){
15609             var last = this.last;
15610             this.selectRange(last, rowIndex, e.ctrlKey);
15611             this.last = last; // reset the last
15612             view.focusRow(rowIndex);
15613         }else{
15614             var isSelected = this.isSelected(rowIndex);
15615             if(e.button !== 0 && isSelected){
15616                 view.focusRow(rowIndex);
15617             }else if(e.ctrlKey && isSelected){
15618                 this.deselectRow(rowIndex);
15619             }else if(!isSelected){
15620                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15621                 view.focusRow(rowIndex);
15622             }
15623         }
15624         this.fireEvent("afterselectionchange", this);
15625     },
15626     // private
15627     handleDragableRowClick :  function(grid, rowIndex, e) 
15628     {
15629         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15630             this.selectRow(rowIndex, false);
15631             grid.view.focusRow(rowIndex);
15632              this.fireEvent("afterselectionchange", this);
15633         }
15634     },
15635     
15636     /**
15637      * Selects multiple rows.
15638      * @param {Array} rows Array of the indexes of the row to select
15639      * @param {Boolean} keepExisting (optional) True to keep existing selections
15640      */
15641     selectRows : function(rows, keepExisting){
15642         if(!keepExisting){
15643             this.clearSelections();
15644         }
15645         for(var i = 0, len = rows.length; i < len; i++){
15646             this.selectRow(rows[i], true);
15647         }
15648     },
15649
15650     /**
15651      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15652      * @param {Number} startRow The index of the first row in the range
15653      * @param {Number} endRow The index of the last row in the range
15654      * @param {Boolean} keepExisting (optional) True to retain existing selections
15655      */
15656     selectRange : function(startRow, endRow, keepExisting){
15657         if(this.locked) return;
15658         if(!keepExisting){
15659             this.clearSelections();
15660         }
15661         if(startRow <= endRow){
15662             for(var i = startRow; i <= endRow; i++){
15663                 this.selectRow(i, true);
15664             }
15665         }else{
15666             for(var i = startRow; i >= endRow; i--){
15667                 this.selectRow(i, true);
15668             }
15669         }
15670     },
15671
15672     /**
15673      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15674      * @param {Number} startRow The index of the first row in the range
15675      * @param {Number} endRow The index of the last row in the range
15676      */
15677     deselectRange : function(startRow, endRow, preventViewNotify){
15678         if(this.locked) return;
15679         for(var i = startRow; i <= endRow; i++){
15680             this.deselectRow(i, preventViewNotify);
15681         }
15682     },
15683
15684     /**
15685      * Selects a row.
15686      * @param {Number} row The index of the row to select
15687      * @param {Boolean} keepExisting (optional) True to keep existing selections
15688      */
15689     selectRow : function(index, keepExisting, preventViewNotify){
15690         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15691         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15692             if(!keepExisting || this.singleSelect){
15693                 this.clearSelections();
15694             }
15695             var r = this.grid.dataSource.getAt(index);
15696             this.selections.add(r);
15697             this.last = this.lastActive = index;
15698             if(!preventViewNotify){
15699                 this.grid.getView().onRowSelect(index);
15700             }
15701             this.fireEvent("rowselect", this, index, r);
15702             this.fireEvent("selectionchange", this);
15703         }
15704     },
15705
15706     /**
15707      * Deselects a row.
15708      * @param {Number} row The index of the row to deselect
15709      */
15710     deselectRow : function(index, preventViewNotify){
15711         if(this.locked) return;
15712         if(this.last == index){
15713             this.last = false;
15714         }
15715         if(this.lastActive == index){
15716             this.lastActive = false;
15717         }
15718         var r = this.grid.dataSource.getAt(index);
15719         this.selections.remove(r);
15720         if(!preventViewNotify){
15721             this.grid.getView().onRowDeselect(index);
15722         }
15723         this.fireEvent("rowdeselect", this, index);
15724         this.fireEvent("selectionchange", this);
15725     },
15726
15727     // private
15728     restoreLast : function(){
15729         if(this._last){
15730             this.last = this._last;
15731         }
15732     },
15733
15734     // private
15735     acceptsNav : function(row, col, cm){
15736         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15737     },
15738
15739     // private
15740     onEditorKey : function(field, e){
15741         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15742         if(k == e.TAB){
15743             e.stopEvent();
15744             ed.completeEdit();
15745             if(e.shiftKey){
15746                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15747             }else{
15748                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15749             }
15750         }else if(k == e.ENTER && !e.ctrlKey){
15751             e.stopEvent();
15752             ed.completeEdit();
15753             if(e.shiftKey){
15754                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15755             }else{
15756                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15757             }
15758         }else if(k == e.ESC){
15759             ed.cancelEdit();
15760         }
15761         if(newCell){
15762             g.startEditing(newCell[0], newCell[1]);
15763         }
15764     }
15765 });/*
15766  * - LGPL
15767  *
15768  * element
15769  * 
15770  */
15771
15772 /**
15773  * @class Roo.bootstrap.MessageBar
15774  * @extends Roo.bootstrap.Component
15775  * Bootstrap MessageBar class
15776  * @cfg {String} html contents of the MessageBar
15777  * @cfg {String} weight (info | success | warning | danger) default info
15778  * @cfg {String} beforeClass insert the bar before the given class
15779  * @cfg {Boolean} closable (true | false) default false
15780  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15781  * 
15782  * @constructor
15783  * Create a new Element
15784  * @param {Object} config The config object
15785  */
15786
15787 Roo.bootstrap.MessageBar = function(config){
15788     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15789 };
15790
15791 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15792     
15793     html: '',
15794     weight: 'info',
15795     closable: false,
15796     fixed: false,
15797     beforeClass: 'bootstrap-sticky-wrap',
15798     
15799     getAutoCreate : function(){
15800         
15801         var cfg = {
15802             tag: 'div',
15803             cls: 'alert alert-dismissable alert-' + this.weight,
15804             cn: [
15805                 {
15806                     tag: 'span',
15807                     cls: 'message',
15808                     html: this.html || ''
15809                 }
15810             ]
15811         }
15812         
15813         if(this.fixed){
15814             cfg.cls += ' alert-messages-fixed';
15815         }
15816         
15817         if(this.closable){
15818             cfg.cn.push({
15819                 tag: 'button',
15820                 cls: 'close',
15821                 html: 'x'
15822             });
15823         }
15824         
15825         return cfg;
15826     },
15827     
15828     onRender : function(ct, position)
15829     {
15830         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15831         
15832         if(!this.el){
15833             var cfg = Roo.apply({},  this.getAutoCreate());
15834             cfg.id = Roo.id();
15835             
15836             if (this.cls) {
15837                 cfg.cls += ' ' + this.cls;
15838             }
15839             if (this.style) {
15840                 cfg.style = this.style;
15841             }
15842             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15843             
15844             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15845         }
15846         
15847         this.el.select('>button.close').on('click', this.hide, this);
15848         
15849     },
15850     
15851     show : function()
15852     {
15853         if (!this.rendered) {
15854             this.render();
15855         }
15856         
15857         this.el.show();
15858         
15859         this.fireEvent('show', this);
15860         
15861     },
15862     
15863     hide : function()
15864     {
15865         if (!this.rendered) {
15866             this.render();
15867         }
15868         
15869         this.el.hide();
15870         
15871         this.fireEvent('hide', this);
15872     },
15873     
15874     update : function()
15875     {
15876 //        var e = this.el.dom.firstChild;
15877 //        
15878 //        if(this.closable){
15879 //            e = e.nextSibling;
15880 //        }
15881 //        
15882 //        e.data = this.html || '';
15883
15884         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15885     }
15886    
15887 });
15888
15889  
15890
15891