roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
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          skip_children = false;
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](false));
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                    Roo.log(this.el);
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                 Roo.log(tree);
234                 if (!cn.el) {
235                     Roo.log('skipping all children');
236                     skip_children = true;
237                 }
238                 
239              } else {
240                  
241                 // actually if flexy:foreach is found, we really want to create 
242                 // multiple copies here...
243                 //Roo.log('render');
244                 //Roo.log(this[cntr]());
245                 cn.render(this[cntr](true));
246              }
247             // then add the element..
248         }
249         
250         
251         // handle the kids..
252         
253         var nitems = [];
254         /*
255         if (typeof (tree.menu) != 'undefined') {
256             tree.menu.parentType = cn.xtype;
257             tree.menu.triggerEl = cn.el;
258             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
259             
260         }
261         */
262         if (!tree.items || !tree.items.length) {
263             cn.items = nitems;
264             return cn;
265         }
266         var items = tree.items;
267         delete tree.items;
268         
269         //Roo.log(items.length);
270             // add the items..
271         if (!skip_children) {    
272             for(var i =0;i < items.length;i++) {
273                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
274             }
275         }
276         
277         cn.items = nitems;
278         
279         return cn;
280     }
281     
282     
283     
284     
285 });
286
287  /*
288  * - LGPL
289  *
290  * Body
291  * 
292  */
293
294 /**
295  * @class Roo.bootstrap.Body
296  * @extends Roo.bootstrap.Component
297  * Bootstrap Body class
298  * 
299  * @constructor
300  * Create a new body
301  * @param {Object} config The config object
302  */
303
304 Roo.bootstrap.Body = function(config){
305     Roo.bootstrap.Body.superclass.constructor.call(this, config);
306     this.el = Roo.get(document.body);
307     if (this.cls && this.cls.length) {
308         Roo.get(document.body).addClass(this.cls);
309     }
310 };
311
312 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
313       
314         autoCreate : {
315         cls: 'container'
316     },
317     onRender : function(ct, position)
318     {
319        /* Roo.log("Roo.bootstrap.Body - onRender");
320         if (this.cls && this.cls.length) {
321             Roo.get(document.body).addClass(this.cls);
322         }
323         // style??? xttr???
324         */
325     }
326     
327     
328  
329    
330 });
331
332  /*
333  * - LGPL
334  *
335  * button group
336  * 
337  */
338
339
340 /**
341  * @class Roo.bootstrap.ButtonGroup
342  * @extends Roo.bootstrap.Component
343  * Bootstrap ButtonGroup class
344  * @cfg {String} size lg | sm | xs (default empty normal)
345  * @cfg {String} align vertical | justified  (default none)
346  * @cfg {String} direction up | down (default down)
347  * @cfg {Boolean} toolbar false | true
348  * @cfg {Boolean} btn true | false
349  * 
350  * 
351  * @constructor
352  * Create a new Input
353  * @param {Object} config The config object
354  */
355
356 Roo.bootstrap.ButtonGroup = function(config){
357     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
358 };
359
360 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
361     
362     size: '',
363     align: '',
364     direction: '',
365     toolbar: false,
366     btn: true,
367
368     getAutoCreate : function(){
369         var cfg = {
370             cls: 'btn-group',
371             html : null
372         }
373         
374         cfg.html = this.html || cfg.html;
375         
376         if (this.toolbar) {
377             cfg = {
378                 cls: 'btn-toolbar',
379                 html: null
380             }
381             
382             return cfg;
383         }
384         
385         if (['vertical','justified'].indexOf(this.align)!==-1) {
386             cfg.cls = 'btn-group-' + this.align;
387             
388             if (this.align == 'justified') {
389                 console.log(this.items);
390             }
391         }
392         
393         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
394             cfg.cls += ' btn-group-' + this.size;
395         }
396         
397         if (this.direction == 'up') {
398             cfg.cls += ' dropup' ;
399         }
400         
401         return cfg;
402     }
403    
404 });
405
406  /*
407  * - LGPL
408  *
409  * button
410  * 
411  */
412
413 /**
414  * @class Roo.bootstrap.Button
415  * @extends Roo.bootstrap.Component
416  * Bootstrap Button class
417  * @cfg {String} html The button content
418  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
419  * @cfg {String} size empty | lg | sm | xs
420  * @cfg {String} tag empty | a | input | submit
421  * @cfg {String} href empty or href
422  * @cfg {Boolean} disabled false | true
423  * @cfg {Boolean} isClose false | true
424  * @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
425  * @cfg {String} badge text for badge
426  * @cfg {String} theme default (or empty) | glow
427  * @cfg {Boolean} inverse false | true
428  * @cfg {Boolean} toggle false | true
429  * @cfg {String} ontext text for on toggle state
430  * @cfg {String} offtext text for off toggle state
431  * @cfg {Boolean} defaulton true | false
432  * @cfg {Boolean} preventDefault (true | false) default true
433  * @cfg {Boolean} removeClass true | false remove the standard class..
434  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
435  * 
436  * @constructor
437  * Create a new button
438  * @param {Object} config The config object
439  */
440
441
442 Roo.bootstrap.Button = function(config){
443     Roo.bootstrap.Button.superclass.constructor.call(this, config);
444     this.addEvents({
445         // raw events
446         /**
447          * @event click
448          * When a butotn is pressed
449          * @param {Roo.EventObject} e
450          */
451         "click" : true,
452          /**
453          * @event toggle
454          * After the button has been toggles
455          * @param {Roo.EventObject} e
456          * @param {boolean} pressed (also available as button.pressed)
457          */
458         "toggle" : true
459     });
460 };
461
462 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
463     html: false,
464     active: false,
465     weight: '',
466     size: '',
467     tag: 'button',
468     href: '',
469     disabled: false,
470     isClose: false,
471     glyphicon: '',
472     badge: '',
473     theme: 'default',
474     inverse: false,
475     
476     toggle: false,
477     ontext: 'ON',
478     offtext: 'OFF',
479     defaulton: true,
480     preventDefault: true,
481     removeClass: false,
482     name: false,
483     target: false,
484     
485     
486     pressed : null,
487      
488     
489     getAutoCreate : function(){
490         
491         var cfg = {
492             tag : 'button',
493             cls : 'roo-button',
494             html: ''
495         };
496         
497         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
498             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
499             this.tag = 'button';
500         } else {
501             cfg.tag = this.tag;
502         }
503         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
504         
505         if (this.toggle == true) {
506             cfg={
507                 tag: 'div',
508                 cls: 'slider-frame roo-button',
509                 cn: [
510                     {
511                         tag: 'span',
512                         'data-on-text':'ON',
513                         'data-off-text':'OFF',
514                         cls: 'slider-button',
515                         html: this.offtext
516                     }
517                 ]
518             };
519             
520             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
521                 cfg.cls += ' '+this.weight;
522             }
523             
524             return cfg;
525         }
526         
527         if (this.isClose) {
528             cfg.cls += ' close';
529             
530             cfg["aria-hidden"] = true;
531             
532             cfg.html = "&times;";
533             
534             return cfg;
535         }
536         
537          
538         if (this.theme==='default') {
539             cfg.cls = 'btn roo-button';
540             
541             //if (this.parentType != 'Navbar') {
542             this.weight = this.weight.length ?  this.weight : 'default';
543             //}
544             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
545                 
546                 cfg.cls += ' btn-' + this.weight;
547             }
548         } else if (this.theme==='glow') {
549             
550             cfg.tag = 'a';
551             cfg.cls = 'btn-glow roo-button';
552             
553             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
554                 
555                 cfg.cls += ' ' + this.weight;
556             }
557         }
558    
559         
560         if (this.inverse) {
561             this.cls += ' inverse';
562         }
563         
564         
565         if (this.active) {
566             cfg.cls += ' active';
567         }
568         
569         if (this.disabled) {
570             cfg.disabled = 'disabled';
571         }
572         
573         if (this.items) {
574             Roo.log('changing to ul' );
575             cfg.tag = 'ul';
576             this.glyphicon = 'caret';
577         }
578         
579         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
580          
581         //gsRoo.log(this.parentType);
582         if (this.parentType === 'Navbar' && !this.parent().bar) {
583             Roo.log('changing to li?');
584             
585             cfg.tag = 'li';
586             
587             cfg.cls = '';
588             cfg.cn =  [{
589                 tag : 'a',
590                 cls : 'roo-button',
591                 html : this.html,
592                 href : this.href || '#'
593             }];
594             if (this.menu) {
595                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
596                 cfg.cls += ' dropdown';
597             }   
598             
599             delete cfg.html;
600             
601         }
602         
603        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
604         
605         if (this.glyphicon) {
606             cfg.html = ' ' + cfg.html;
607             
608             cfg.cn = [
609                 {
610                     tag: 'span',
611                     cls: 'glyphicon glyphicon-' + this.glyphicon
612                 }
613             ];
614         }
615         
616         if (this.badge) {
617             cfg.html += ' ';
618             
619             cfg.tag = 'a';
620             
621 //            cfg.cls='btn roo-button';
622             
623             cfg.href=this.href;
624             
625             var value = cfg.html;
626             
627             if(this.glyphicon){
628                 value = {
629                             tag: 'span',
630                             cls: 'glyphicon glyphicon-' + this.glyphicon,
631                             html: this.html
632                         };
633                 
634             }
635             
636             cfg.cn = [
637                 value,
638                 {
639                     tag: 'span',
640                     cls: 'badge',
641                     html: this.badge
642                 }
643             ];
644             
645             cfg.html='';
646         }
647         
648         if (this.menu) {
649             cfg.cls += ' dropdown';
650             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
651         }
652         
653         if (cfg.tag !== 'a' && this.href !== '') {
654             throw "Tag must be a to set href.";
655         } else if (this.href.length > 0) {
656             cfg.href = this.href;
657         }
658         
659         if(this.removeClass){
660             cfg.cls = '';
661         }
662         
663         if(this.target){
664             cfg.target = this.target;
665         }
666         
667         return cfg;
668     },
669     initEvents: function() {
670        // Roo.log('init events?');
671 //        Roo.log(this.el.dom);
672         // add the menu...
673         
674         if (typeof (this.menu) != 'undefined') {
675             this.menu.parentType = this.xtype;
676             this.menu.triggerEl = this.el;
677             this.addxtype(Roo.apply({}, this.menu));
678         }
679
680
681        if (this.el.hasClass('roo-button')) {
682             this.el.on('click', this.onClick, this);
683        } else {
684             this.el.select('.roo-button').on('click', this.onClick, this);
685        }
686        
687        if(this.removeClass){
688            this.el.on('click', this.onClick, this);
689        }
690        
691        this.el.enableDisplayMode();
692         
693     },
694     onClick : function(e)
695     {
696         if (this.disabled) {
697             return;
698         }
699         
700         Roo.log('button on click ');
701         if(this.preventDefault){
702             e.preventDefault();
703         }
704         if (this.pressed === true || this.pressed === false) {
705             this.pressed = !this.pressed;
706             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
707             this.fireEvent('toggle', this, e, this.pressed);
708         }
709         
710         
711         this.fireEvent('click', this, e);
712     },
713     
714     /**
715      * Enables this button
716      */
717     enable : function()
718     {
719         this.disabled = false;
720         this.el.removeClass('disabled');
721     },
722     
723     /**
724      * Disable this button
725      */
726     disable : function()
727     {
728         this.disabled = true;
729         this.el.addClass('disabled');
730     },
731      /**
732      * sets the active state on/off, 
733      * @param {Boolean} state (optional) Force a particular state
734      */
735     setActive : function(v) {
736         
737         this.el[v ? 'addClass' : 'removeClass']('active');
738     },
739      /**
740      * toggles the current active state 
741      */
742     toggleActive : function()
743     {
744        var active = this.el.hasClass('active');
745        this.setActive(!active);
746        
747         
748     },
749     setText : function(str)
750     {
751         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
752     },
753     getText : function()
754     {
755         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
756     },
757     hide: function() {
758        
759      
760         this.el.hide();   
761     },
762     show: function() {
763        
764         this.el.show();   
765     }
766     
767     
768 });
769
770  /*
771  * - LGPL
772  *
773  * column
774  * 
775  */
776
777 /**
778  * @class Roo.bootstrap.Column
779  * @extends Roo.bootstrap.Component
780  * Bootstrap Column class
781  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
782  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
783  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
784  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
785  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
786  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
787  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
788  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
789  *
790  * 
791  * @cfg {Boolean} hidden (true|false) hide the element
792  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
793  * @cfg {String} fa (ban|check|...) font awesome icon
794  * @cfg {Number} fasize (1|2|....) font awsome size
795
796  * @cfg {String} icon (info-sign|check|...) glyphicon name
797
798  * @cfg {String} html content of column.
799  * 
800  * @constructor
801  * Create a new Column
802  * @param {Object} config The config object
803  */
804
805 Roo.bootstrap.Column = function(config){
806     Roo.bootstrap.Column.superclass.constructor.call(this, config);
807 };
808
809 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
810     
811     xs: false,
812     sm: false,
813     md: false,
814     lg: false,
815     xsoff: false,
816     smoff: false,
817     mdoff: false,
818     lgoff: false,
819     html: '',
820     offset: 0,
821     alert: false,
822     fa: false,
823     icon : false,
824     hidden : false,
825     fasize : 1,
826     
827     getAutoCreate : function(){
828         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
829         
830         cfg = {
831             tag: 'div',
832             cls: 'column'
833         };
834         
835         var settings=this;
836         ['xs','sm','md','lg'].map(function(size){
837             //Roo.log( size + ':' + settings[size]);
838             
839             if (settings[size+'off'] !== false) {
840                 cfg.cls += ' col-' + settings[size+'off'] + '-offset';
841             }
842             
843             if (settings[size] === false) {
844                 return;
845             }
846             Roo.log(settings[size]);
847             if (!settings[size]) { // 0 = hidden
848                 cfg.cls += ' hidden-' + size;
849                 return;
850             }
851             cfg.cls += ' col-' + size + '-' + settings[size];
852             
853         });
854         
855         if (this.hidden) {
856             cfg.cls += ' hidden';
857         }
858         
859         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
860             cfg.cls +=' alert alert-' + this.alert;
861         }
862         
863         
864         if (this.html.length) {
865             cfg.html = this.html;
866         }
867         if (this.fa) {
868             var fasize = '';
869             if (this.fasize > 1) {
870                 fasize = ' fa-' + this.fasize + 'x';
871             }
872             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
873             
874             
875         }
876         if (this.icon) {
877             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
878         }
879         
880         return cfg;
881     }
882    
883 });
884
885  
886
887  /*
888  * - LGPL
889  *
890  * page container.
891  * 
892  */
893
894
895 /**
896  * @class Roo.bootstrap.Container
897  * @extends Roo.bootstrap.Component
898  * Bootstrap Container class
899  * @cfg {Boolean} jumbotron is it a jumbotron element
900  * @cfg {String} html content of element
901  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
902  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
903  * @cfg {String} header content of header (for panel)
904  * @cfg {String} footer content of footer (for panel)
905  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
906  * @cfg {String} tag (header|aside|section) type of HTML tag.
907  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
908  * @cfg {String} fa (ban|check|...) font awesome icon
909  * @cfg {String} icon (info-sign|check|...) glyphicon name
910  * @cfg {Boolean} hidden (true|false) hide the element
911
912  *     
913  * @constructor
914  * Create a new Container
915  * @param {Object} config The config object
916  */
917
918 Roo.bootstrap.Container = function(config){
919     Roo.bootstrap.Container.superclass.constructor.call(this, config);
920 };
921
922 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
923     
924     jumbotron : false,
925     well: '',
926     panel : '',
927     header: '',
928     footer : '',
929     sticky: '',
930     tag : false,
931     alert : false,
932     fa: false,
933     icon : false,
934   
935      
936     getChildContainer : function() {
937         
938         if(!this.el){
939             return false;
940         }
941         
942         if (this.panel.length) {
943             return this.el.select('.panel-body',true).first();
944         }
945         
946         return this.el;
947     },
948     
949     
950     getAutoCreate : function(){
951         
952         var cfg = {
953             tag : this.tag || 'div',
954             html : '',
955             cls : ''
956         };
957         if (this.jumbotron) {
958             cfg.cls = 'jumbotron';
959         }
960         
961         
962         
963         // - this is applied by the parent..
964         //if (this.cls) {
965         //    cfg.cls = this.cls + '';
966         //}
967         
968         if (this.sticky.length) {
969             
970             var bd = Roo.get(document.body);
971             if (!bd.hasClass('bootstrap-sticky')) {
972                 bd.addClass('bootstrap-sticky');
973                 Roo.select('html',true).setStyle('height', '100%');
974             }
975              
976             cfg.cls += 'bootstrap-sticky-' + this.sticky;
977         }
978         
979         
980         if (this.well.length) {
981             switch (this.well) {
982                 case 'lg':
983                 case 'sm':
984                     cfg.cls +=' well well-' +this.well;
985                     break;
986                 default:
987                     cfg.cls +=' well';
988                     break;
989             }
990         }
991         
992         if (this.hidden) {
993             cfg.cls += ' hidden';
994         }
995         
996         
997         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
998             cfg.cls +=' alert alert-' + this.alert;
999         }
1000         
1001         var body = cfg;
1002         
1003         if (this.panel.length) {
1004             cfg.cls += ' panel panel-' + this.panel;
1005             cfg.cn = [];
1006             if (this.header.length) {
1007                 cfg.cn.push({
1008                     
1009                     cls : 'panel-heading',
1010                     cn : [{
1011                         tag: 'h3',
1012                         cls : 'panel-title',
1013                         html : this.header
1014                     }]
1015                     
1016                 });
1017             }
1018             body = false;
1019             cfg.cn.push({
1020                 cls : 'panel-body',
1021                 html : this.html
1022             });
1023             
1024             
1025             if (this.footer.length) {
1026                 cfg.cn.push({
1027                     cls : 'panel-footer',
1028                     html : this.footer
1029                     
1030                 });
1031             }
1032             
1033         }
1034         
1035         if (body) {
1036             body.html = this.html || cfg.html;
1037             // prefix with the icons..
1038             if (this.fa) {
1039                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1040             }
1041             if (this.icon) {
1042                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1043             }
1044             
1045             
1046         }
1047         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1048             cfg.cls =  'container';
1049         }
1050         
1051         return cfg;
1052     },
1053     
1054     titleEl : function()
1055     {
1056         if(!this.el || !this.panel.length || !this.header.length){
1057             return;
1058         }
1059         
1060         return this.el.select('.panel-title',true).first();
1061     },
1062     
1063     setTitle : function(v)
1064     {
1065         var titleEl = this.titleEl();
1066         
1067         if(!titleEl){
1068             return;
1069         }
1070         
1071         titleEl.dom.innerHTML = v;
1072     },
1073     
1074     getTitle : function()
1075     {
1076         
1077         var titleEl = this.titleEl();
1078         
1079         if(!titleEl){
1080             return '';
1081         }
1082         
1083         return titleEl.dom.innerHTML;
1084     }
1085    
1086 });
1087
1088  /*
1089  * - LGPL
1090  *
1091  * image
1092  * 
1093  */
1094
1095
1096 /**
1097  * @class Roo.bootstrap.Img
1098  * @extends Roo.bootstrap.Component
1099  * Bootstrap Img class
1100  * @cfg {Boolean} imgResponsive false | true
1101  * @cfg {String} border rounded | circle | thumbnail
1102  * @cfg {String} src image source
1103  * @cfg {String} alt image alternative text
1104  * @cfg {String} href a tag href
1105  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1106  * 
1107  * @constructor
1108  * Create a new Input
1109  * @param {Object} config The config object
1110  */
1111
1112 Roo.bootstrap.Img = function(config){
1113     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1114     
1115     this.addEvents({
1116         // img events
1117         /**
1118          * @event click
1119          * The img click event for the img.
1120          * @param {Roo.EventObject} e
1121          */
1122         "click" : true
1123     });
1124 };
1125
1126 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1127     
1128     imgResponsive: true,
1129     border: '',
1130     src: '',
1131     href: false,
1132     target: false,
1133
1134     getAutoCreate : function(){
1135         
1136         var cfg = {
1137             tag: 'img',
1138             cls: (this.imgResponsive) ? 'img-responsive' : '',
1139             html : null
1140         }
1141         
1142         cfg.html = this.html || cfg.html;
1143         
1144         cfg.src = this.src || cfg.src;
1145         
1146         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1147             cfg.cls += ' img-' + this.border;
1148         }
1149         
1150         if(this.alt){
1151             cfg.alt = this.alt;
1152         }
1153         
1154         if(this.href){
1155             var a = {
1156                 tag: 'a',
1157                 href: this.href,
1158                 cn: [
1159                     cfg
1160                 ]
1161             }
1162             
1163             if(this.target){
1164                 a.target = this.target;
1165             }
1166             
1167         }
1168         
1169         
1170         return (this.href) ? a : cfg;
1171     },
1172     
1173     initEvents: function() {
1174         
1175         if(!this.href){
1176             this.el.on('click', this.onClick, this);
1177         }
1178     },
1179     
1180     onClick : function(e)
1181     {
1182         Roo.log('img onclick');
1183         this.fireEvent('click', this, e);
1184     }
1185    
1186 });
1187
1188  /*
1189  * - LGPL
1190  *
1191  * image
1192  * 
1193  */
1194
1195
1196 /**
1197  * @class Roo.bootstrap.Link
1198  * @extends Roo.bootstrap.Component
1199  * Bootstrap Link Class
1200  * @cfg {String} alt image alternative text
1201  * @cfg {String} href a tag href
1202  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1203  * @cfg {String} html the content of the link.
1204  * @cfg {String} anchor name for the anchor link
1205
1206  * @cfg {Boolean} preventDefault (true | false) default false
1207
1208  * 
1209  * @constructor
1210  * Create a new Input
1211  * @param {Object} config The config object
1212  */
1213
1214 Roo.bootstrap.Link = function(config){
1215     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1216     
1217     this.addEvents({
1218         // img events
1219         /**
1220          * @event click
1221          * The img click event for the img.
1222          * @param {Roo.EventObject} e
1223          */
1224         "click" : true
1225     });
1226 };
1227
1228 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1229     
1230     href: false,
1231     target: false,
1232     preventDefault: false,
1233     anchor : false,
1234     alt : false,
1235
1236     getAutoCreate : function()
1237     {
1238         
1239         var cfg = {
1240             tag: 'a'
1241         };
1242         // anchor's do not require html/href...
1243         if (this.anchor === false) {
1244             cfg.html = this.html || 'html-missing';
1245             cfg.href = this.href || '#';
1246         } else {
1247             cfg.name = this.anchor;
1248             if (this.html !== false) {
1249                 cfg.html = this.html;
1250             }
1251             if (this.href !== false) {
1252                 cfg.href = this.href;
1253             }
1254         }
1255         
1256         if(this.alt !== false){
1257             cfg.alt = this.alt;
1258         }
1259         
1260         
1261         if(this.target !== false) {
1262             cfg.target = this.target;
1263         }
1264         
1265         return cfg;
1266     },
1267     
1268     initEvents: function() {
1269         
1270         if(!this.href || this.preventDefault){
1271             this.el.on('click', this.onClick, this);
1272         }
1273     },
1274     
1275     onClick : function(e)
1276     {
1277         if(this.preventDefault){
1278             e.preventDefault();
1279         }
1280         //Roo.log('img onclick');
1281         this.fireEvent('click', this, e);
1282     }
1283    
1284 });
1285
1286  /*
1287  * - LGPL
1288  *
1289  * header
1290  * 
1291  */
1292
1293 /**
1294  * @class Roo.bootstrap.Header
1295  * @extends Roo.bootstrap.Component
1296  * Bootstrap Header class
1297  * @cfg {String} html content of header
1298  * @cfg {Number} level (1|2|3|4|5|6) default 1
1299  * 
1300  * @constructor
1301  * Create a new Header
1302  * @param {Object} config The config object
1303  */
1304
1305
1306 Roo.bootstrap.Header  = function(config){
1307     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1308 };
1309
1310 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1311     
1312     //href : false,
1313     html : false,
1314     level : 1,
1315     
1316     
1317     
1318     getAutoCreate : function(){
1319         
1320         var cfg = {
1321             tag: 'h' + (1 *this.level),
1322             html: this.html || 'fill in html'
1323         } ;
1324         
1325         return cfg;
1326     }
1327    
1328 });
1329
1330  
1331
1332  /*
1333  * Based on:
1334  * Ext JS Library 1.1.1
1335  * Copyright(c) 2006-2007, Ext JS, LLC.
1336  *
1337  * Originally Released Under LGPL - original licence link has changed is not relivant.
1338  *
1339  * Fork - LGPL
1340  * <script type="text/javascript">
1341  */
1342  
1343 /**
1344  * @class Roo.bootstrap.MenuMgr
1345  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1346  * @singleton
1347  */
1348 Roo.bootstrap.MenuMgr = function(){
1349    var menus, active, groups = {}, attached = false, lastShow = new Date();
1350
1351    // private - called when first menu is created
1352    function init(){
1353        menus = {};
1354        active = new Roo.util.MixedCollection();
1355        Roo.get(document).addKeyListener(27, function(){
1356            if(active.length > 0){
1357                hideAll();
1358            }
1359        });
1360    }
1361
1362    // private
1363    function hideAll(){
1364        if(active && active.length > 0){
1365            var c = active.clone();
1366            c.each(function(m){
1367                m.hide();
1368            });
1369        }
1370    }
1371
1372    // private
1373    function onHide(m){
1374        active.remove(m);
1375        if(active.length < 1){
1376            Roo.get(document).un("mouseup", onMouseDown);
1377             
1378            attached = false;
1379        }
1380    }
1381
1382    // private
1383    function onShow(m){
1384        var last = active.last();
1385        lastShow = new Date();
1386        active.add(m);
1387        if(!attached){
1388           Roo.get(document).on("mouseup", onMouseDown);
1389            
1390            attached = true;
1391        }
1392        if(m.parentMenu){
1393           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1394           m.parentMenu.activeChild = m;
1395        }else if(last && last.isVisible()){
1396           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1397        }
1398    }
1399
1400    // private
1401    function onBeforeHide(m){
1402        if(m.activeChild){
1403            m.activeChild.hide();
1404        }
1405        if(m.autoHideTimer){
1406            clearTimeout(m.autoHideTimer);
1407            delete m.autoHideTimer;
1408        }
1409    }
1410
1411    // private
1412    function onBeforeShow(m){
1413        var pm = m.parentMenu;
1414        if(!pm && !m.allowOtherMenus){
1415            hideAll();
1416        }else if(pm && pm.activeChild && active != m){
1417            pm.activeChild.hide();
1418        }
1419    }
1420
1421    // private
1422    function onMouseDown(e){
1423         Roo.log("on MouseDown");
1424         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1425            hideAll();
1426         }
1427         
1428         
1429    }
1430
1431    // private
1432    function onBeforeCheck(mi, state){
1433        if(state){
1434            var g = groups[mi.group];
1435            for(var i = 0, l = g.length; i < l; i++){
1436                if(g[i] != mi){
1437                    g[i].setChecked(false);
1438                }
1439            }
1440        }
1441    }
1442
1443    return {
1444
1445        /**
1446         * Hides all menus that are currently visible
1447         */
1448        hideAll : function(){
1449             hideAll();  
1450        },
1451
1452        // private
1453        register : function(menu){
1454            if(!menus){
1455                init();
1456            }
1457            menus[menu.id] = menu;
1458            menu.on("beforehide", onBeforeHide);
1459            menu.on("hide", onHide);
1460            menu.on("beforeshow", onBeforeShow);
1461            menu.on("show", onShow);
1462            var g = menu.group;
1463            if(g && menu.events["checkchange"]){
1464                if(!groups[g]){
1465                    groups[g] = [];
1466                }
1467                groups[g].push(menu);
1468                menu.on("checkchange", onCheck);
1469            }
1470        },
1471
1472         /**
1473          * Returns a {@link Roo.menu.Menu} object
1474          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1475          * be used to generate and return a new Menu instance.
1476          */
1477        get : function(menu){
1478            if(typeof menu == "string"){ // menu id
1479                return menus[menu];
1480            }else if(menu.events){  // menu instance
1481                return menu;
1482            }
1483            /*else if(typeof menu.length == 'number'){ // array of menu items?
1484                return new Roo.bootstrap.Menu({items:menu});
1485            }else{ // otherwise, must be a config
1486                return new Roo.bootstrap.Menu(menu);
1487            }
1488            */
1489            return false;
1490        },
1491
1492        // private
1493        unregister : function(menu){
1494            delete menus[menu.id];
1495            menu.un("beforehide", onBeforeHide);
1496            menu.un("hide", onHide);
1497            menu.un("beforeshow", onBeforeShow);
1498            menu.un("show", onShow);
1499            var g = menu.group;
1500            if(g && menu.events["checkchange"]){
1501                groups[g].remove(menu);
1502                menu.un("checkchange", onCheck);
1503            }
1504        },
1505
1506        // private
1507        registerCheckable : function(menuItem){
1508            var g = menuItem.group;
1509            if(g){
1510                if(!groups[g]){
1511                    groups[g] = [];
1512                }
1513                groups[g].push(menuItem);
1514                menuItem.on("beforecheckchange", onBeforeCheck);
1515            }
1516        },
1517
1518        // private
1519        unregisterCheckable : function(menuItem){
1520            var g = menuItem.group;
1521            if(g){
1522                groups[g].remove(menuItem);
1523                menuItem.un("beforecheckchange", onBeforeCheck);
1524            }
1525        }
1526    };
1527 }();/*
1528  * - LGPL
1529  *
1530  * menu
1531  * 
1532  */
1533
1534 /**
1535  * @class Roo.bootstrap.Menu
1536  * @extends Roo.bootstrap.Component
1537  * Bootstrap Menu class - container for MenuItems
1538  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1539  * 
1540  * @constructor
1541  * Create a new Menu
1542  * @param {Object} config The config object
1543  */
1544
1545
1546 Roo.bootstrap.Menu = function(config){
1547     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1548     if (this.registerMenu) {
1549         Roo.bootstrap.MenuMgr.register(this);
1550     }
1551     this.addEvents({
1552         /**
1553          * @event beforeshow
1554          * Fires before this menu is displayed
1555          * @param {Roo.menu.Menu} this
1556          */
1557         beforeshow : true,
1558         /**
1559          * @event beforehide
1560          * Fires before this menu is hidden
1561          * @param {Roo.menu.Menu} this
1562          */
1563         beforehide : true,
1564         /**
1565          * @event show
1566          * Fires after this menu is displayed
1567          * @param {Roo.menu.Menu} this
1568          */
1569         show : true,
1570         /**
1571          * @event hide
1572          * Fires after this menu is hidden
1573          * @param {Roo.menu.Menu} this
1574          */
1575         hide : true,
1576         /**
1577          * @event click
1578          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1579          * @param {Roo.menu.Menu} this
1580          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1581          * @param {Roo.EventObject} e
1582          */
1583         click : true,
1584         /**
1585          * @event mouseover
1586          * Fires when the mouse is hovering over this menu
1587          * @param {Roo.menu.Menu} this
1588          * @param {Roo.EventObject} e
1589          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1590          */
1591         mouseover : true,
1592         /**
1593          * @event mouseout
1594          * Fires when the mouse exits this menu
1595          * @param {Roo.menu.Menu} this
1596          * @param {Roo.EventObject} e
1597          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1598          */
1599         mouseout : true,
1600         /**
1601          * @event itemclick
1602          * Fires when a menu item contained in this menu is clicked
1603          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1604          * @param {Roo.EventObject} e
1605          */
1606         itemclick: true
1607     });
1608     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1609 };
1610
1611 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1612     
1613    /// html : false,
1614     //align : '',
1615     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1616     type: false,
1617     /**
1618      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1619      */
1620     registerMenu : true,
1621     
1622     menuItems :false, // stores the menu items..
1623     
1624     hidden:true,
1625     
1626     parentMenu : false,
1627     
1628     getChildContainer : function() {
1629         return this.el;  
1630     },
1631     
1632     getAutoCreate : function(){
1633          
1634         //if (['right'].indexOf(this.align)!==-1) {
1635         //    cfg.cn[1].cls += ' pull-right'
1636         //}
1637         
1638         
1639         var cfg = {
1640             tag : 'ul',
1641             cls : 'dropdown-menu' ,
1642             style : 'z-index:1000'
1643             
1644         }
1645         
1646         if (this.type === 'submenu') {
1647             cfg.cls = 'submenu active';
1648         }
1649         if (this.type === 'treeview') {
1650             cfg.cls = 'treeview-menu';
1651         }
1652         
1653         return cfg;
1654     },
1655     initEvents : function() {
1656         
1657        // Roo.log("ADD event");
1658        // Roo.log(this.triggerEl.dom);
1659         this.triggerEl.on('click', this.onTriggerPress, this);
1660         this.triggerEl.addClass('dropdown-toggle');
1661         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1662
1663         this.el.on("mouseover", this.onMouseOver, this);
1664         this.el.on("mouseout", this.onMouseOut, this);
1665         
1666         
1667     },
1668     findTargetItem : function(e){
1669         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1670         if(!t){
1671             return false;
1672         }
1673         //Roo.log(t);         Roo.log(t.id);
1674         if(t && t.id){
1675             //Roo.log(this.menuitems);
1676             return this.menuitems.get(t.id);
1677             
1678             //return this.items.get(t.menuItemId);
1679         }
1680         
1681         return false;
1682     },
1683     onClick : function(e){
1684         Roo.log("menu.onClick");
1685         var t = this.findTargetItem(e);
1686         if(!t){
1687             return;
1688         }
1689         Roo.log(e);
1690         /*
1691         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1692             if(t == this.activeItem && t.shouldDeactivate(e)){
1693                 this.activeItem.deactivate();
1694                 delete this.activeItem;
1695                 return;
1696             }
1697             if(t.canActivate){
1698                 this.setActiveItem(t, true);
1699             }
1700             return;
1701             
1702             
1703         }
1704         */
1705         Roo.log('pass click event');
1706         
1707         t.onClick(e);
1708         
1709         this.fireEvent("click", this, t, e);
1710         
1711         this.hide();
1712     },
1713      onMouseOver : function(e){
1714         var t  = this.findTargetItem(e);
1715         //Roo.log(t);
1716         //if(t){
1717         //    if(t.canActivate && !t.disabled){
1718         //        this.setActiveItem(t, true);
1719         //    }
1720         //}
1721         
1722         this.fireEvent("mouseover", this, e, t);
1723     },
1724     isVisible : function(){
1725         return !this.hidden;
1726     },
1727      onMouseOut : function(e){
1728         var t  = this.findTargetItem(e);
1729         
1730         //if(t ){
1731         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1732         //        this.activeItem.deactivate();
1733         //        delete this.activeItem;
1734         //    }
1735         //}
1736         this.fireEvent("mouseout", this, e, t);
1737     },
1738     
1739     
1740     /**
1741      * Displays this menu relative to another element
1742      * @param {String/HTMLElement/Roo.Element} element The element to align to
1743      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1744      * the element (defaults to this.defaultAlign)
1745      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1746      */
1747     show : function(el, pos, parentMenu){
1748         this.parentMenu = parentMenu;
1749         if(!this.el){
1750             this.render();
1751         }
1752         this.fireEvent("beforeshow", this);
1753         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1754     },
1755      /**
1756      * Displays this menu at a specific xy position
1757      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1758      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1759      */
1760     showAt : function(xy, parentMenu, /* private: */_e){
1761         this.parentMenu = parentMenu;
1762         if(!this.el){
1763             this.render();
1764         }
1765         if(_e !== false){
1766             this.fireEvent("beforeshow", this);
1767             
1768             //xy = this.el.adjustForConstraints(xy);
1769         }
1770         //this.el.setXY(xy);
1771         //this.el.show();
1772         this.hideMenuItems();
1773         this.hidden = false;
1774         this.triggerEl.addClass('open');
1775         this.focus();
1776         this.fireEvent("show", this);
1777     },
1778     
1779     focus : function(){
1780         return;
1781         if(!this.hidden){
1782             this.doFocus.defer(50, this);
1783         }
1784     },
1785
1786     doFocus : function(){
1787         if(!this.hidden){
1788             this.focusEl.focus();
1789         }
1790     },
1791
1792     /**
1793      * Hides this menu and optionally all parent menus
1794      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1795      */
1796     hide : function(deep){
1797         
1798         this.hideMenuItems();
1799         if(this.el && this.isVisible()){
1800             this.fireEvent("beforehide", this);
1801             if(this.activeItem){
1802                 this.activeItem.deactivate();
1803                 this.activeItem = null;
1804             }
1805             this.triggerEl.removeClass('open');;
1806             this.hidden = true;
1807             this.fireEvent("hide", this);
1808         }
1809         if(deep === true && this.parentMenu){
1810             this.parentMenu.hide(true);
1811         }
1812     },
1813     
1814     onTriggerPress  : function(e)
1815     {
1816         
1817         Roo.log('trigger press');
1818         //Roo.log(e.getTarget());
1819        // Roo.log(this.triggerEl.dom);
1820         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1821             return;
1822         }
1823         if (this.isVisible()) {
1824             Roo.log('hide');
1825             this.hide();
1826         } else {
1827             this.show(this.triggerEl, false, false);
1828         }
1829         
1830         
1831     },
1832     
1833          
1834        
1835     
1836     hideMenuItems : function()
1837     {
1838         //$(backdrop).remove()
1839         Roo.select('.open',true).each(function(aa) {
1840             
1841             aa.removeClass('open');
1842           //var parent = getParent($(this))
1843           //var relatedTarget = { relatedTarget: this }
1844           
1845            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1846           //if (e.isDefaultPrevented()) return
1847            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1848         })
1849     },
1850     addxtypeChild : function (tree, cntr) {
1851         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1852           
1853         this.menuitems.add(comp);
1854         return comp;
1855
1856     },
1857     getEl : function()
1858     {
1859         Roo.log(this.el);
1860         return this.el;
1861     }
1862 });
1863
1864  
1865  /*
1866  * - LGPL
1867  *
1868  * menu item
1869  * 
1870  */
1871
1872
1873 /**
1874  * @class Roo.bootstrap.MenuItem
1875  * @extends Roo.bootstrap.Component
1876  * Bootstrap MenuItem class
1877  * @cfg {String} html the menu label
1878  * @cfg {String} href the link
1879  * @cfg {Boolean} preventDefault (true | false) default true
1880  * 
1881  * 
1882  * @constructor
1883  * Create a new MenuItem
1884  * @param {Object} config The config object
1885  */
1886
1887
1888 Roo.bootstrap.MenuItem = function(config){
1889     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1890     this.addEvents({
1891         // raw events
1892         /**
1893          * @event click
1894          * The raw click event for the entire grid.
1895          * @param {Roo.EventObject} e
1896          */
1897         "click" : true
1898     });
1899 };
1900
1901 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1902     
1903     href : false,
1904     html : false,
1905     preventDefault: true,
1906     
1907     getAutoCreate : function(){
1908         var cfg= {
1909             tag: 'li',
1910             cls: 'dropdown-menu-item',
1911             cn: [
1912                     {
1913                         tag : 'a',
1914                         href : '#',
1915                         html : 'Link'
1916                     }
1917                 ]
1918         };
1919         if (this.parent().type == 'treeview') {
1920             cfg.cls = 'treeview-menu';
1921         }
1922         
1923         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1924         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1925         return cfg;
1926     },
1927     
1928     initEvents: function() {
1929         
1930         //this.el.select('a').on('click', this.onClick, this);
1931         
1932     },
1933     onClick : function(e)
1934     {
1935         Roo.log('item on click ');
1936         //if(this.preventDefault){
1937         //    e.preventDefault();
1938         //}
1939         //this.parent().hideMenuItems();
1940         
1941         this.fireEvent('click', this, e);
1942     },
1943     getEl : function()
1944     {
1945         return this.el;
1946     }
1947 });
1948
1949  
1950
1951  /*
1952  * - LGPL
1953  *
1954  * menu separator
1955  * 
1956  */
1957
1958
1959 /**
1960  * @class Roo.bootstrap.MenuSeparator
1961  * @extends Roo.bootstrap.Component
1962  * Bootstrap MenuSeparator class
1963  * 
1964  * @constructor
1965  * Create a new MenuItem
1966  * @param {Object} config The config object
1967  */
1968
1969
1970 Roo.bootstrap.MenuSeparator = function(config){
1971     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1972 };
1973
1974 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1975     
1976     getAutoCreate : function(){
1977         var cfg = {
1978             cls: 'divider',
1979             tag : 'li'
1980         };
1981         
1982         return cfg;
1983     }
1984    
1985 });
1986
1987  
1988
1989  
1990 /*
1991 <div class="modal fade">
1992   <div class="modal-dialog">
1993     <div class="modal-content">
1994       <div class="modal-header">
1995         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1996         <h4 class="modal-title">Modal title</h4>
1997       </div>
1998       <div class="modal-body">
1999         <p>One fine body&hellip;</p>
2000       </div>
2001       <div class="modal-footer">
2002         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2003         <button type="button" class="btn btn-primary">Save changes</button>
2004       </div>
2005     </div><!-- /.modal-content -->
2006   </div><!-- /.modal-dialog -->
2007 </div><!-- /.modal -->
2008 */
2009 /*
2010  * - LGPL
2011  *
2012  * page contgainer.
2013  * 
2014  */
2015
2016 /**
2017  * @class Roo.bootstrap.Modal
2018  * @extends Roo.bootstrap.Component
2019  * Bootstrap Modal class
2020  * @cfg {String} title Title of dialog
2021  * @cfg {Boolean} specificTitle (true|false) default false
2022  * @cfg {Array} buttons Array of buttons or standard button set..
2023  * @cfg {String} buttonPosition (left|right|center) default right
2024  * 
2025  * @constructor
2026  * Create a new Modal Dialog
2027  * @param {Object} config The config object
2028  */
2029
2030 Roo.bootstrap.Modal = function(config){
2031     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2032     this.addEvents({
2033         // raw events
2034         /**
2035          * @event btnclick
2036          * The raw btnclick event for the button
2037          * @param {Roo.EventObject} e
2038          */
2039         "btnclick" : true
2040     });
2041     this.buttons = this.buttons || [];
2042 };
2043
2044 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2045     
2046     title : 'test dialog',
2047    
2048     buttons : false,
2049     
2050     // set on load...
2051     body:  false,
2052     
2053     specificTitle: false,
2054     
2055     buttonPosition: 'right',
2056     
2057     onRender : function(ct, position)
2058     {
2059         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2060      
2061         if(!this.el){
2062             var cfg = Roo.apply({},  this.getAutoCreate());
2063             cfg.id = Roo.id();
2064             //if(!cfg.name){
2065             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2066             //}
2067             //if (!cfg.name.length) {
2068             //    delete cfg.name;
2069            // }
2070             if (this.cls) {
2071                 cfg.cls += ' ' + this.cls;
2072             }
2073             if (this.style) {
2074                 cfg.style = this.style;
2075             }
2076             this.el = Roo.get(document.body).createChild(cfg, position);
2077         }
2078         //var type = this.el.dom.type;
2079         
2080         if(this.tabIndex !== undefined){
2081             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2082         }
2083         
2084         
2085         
2086         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2087         this.maskEl.enableDisplayMode("block");
2088         this.maskEl.hide();
2089         //this.el.addClass("x-dlg-modal");
2090     
2091         if (this.buttons.length) {
2092             Roo.each(this.buttons, function(bb) {
2093                 b = Roo.apply({}, bb);
2094                 b.xns = b.xns || Roo.bootstrap;
2095                 b.xtype = b.xtype || 'Button';
2096                 if (typeof(b.listeners) == 'undefined') {
2097                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2098                 }
2099                 
2100                 var btn = Roo.factory(b);
2101                 
2102                 btn.onRender(this.el.select('.modal-footer div').first());
2103                 
2104             },this);
2105         }
2106         // render the children.
2107         var nitems = [];
2108         
2109         if(typeof(this.items) != 'undefined'){
2110             var items = this.items;
2111             delete this.items;
2112
2113             for(var i =0;i < items.length;i++) {
2114                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2115             }
2116         }
2117         
2118         this.items = nitems;
2119         
2120         this.body = this.el.select('.modal-body',true).first();
2121         this.close = this.el.select('.modal-header .close', true).first();
2122         this.footer = this.el.select('.modal-footer',true).first();
2123         this.initEvents();
2124         //this.el.addClass([this.fieldClass, this.cls]);
2125         
2126     },
2127     getAutoCreate : function(){
2128         
2129         
2130         var bdy = {
2131                 cls : 'modal-body',
2132                 html : this.html || ''
2133         };
2134         
2135         var title = {
2136             tag: 'h4',
2137             cls : 'modal-title',
2138             html : this.title
2139         };
2140         
2141         if(this.specificTitle){
2142             title = this.title;
2143         };
2144         
2145         return modal = {
2146             cls: "modal fade",
2147             style : 'display: none',
2148             cn : [
2149                 {
2150                     cls: "modal-dialog",
2151                     cn : [
2152                         {
2153                             cls : "modal-content",
2154                             cn : [
2155                                 {
2156                                     cls : 'modal-header',
2157                                     cn : [
2158                                         {
2159                                             tag: 'button',
2160                                             cls : 'close',
2161                                             html : '&times'
2162                                         },
2163                                         title
2164                                     ]
2165                                 },
2166                                 bdy,
2167                                 {
2168                                     cls : 'modal-footer',
2169                                     cn : [
2170                                         {
2171                                             tag: 'div',
2172                                             cls: 'btn-' + this.buttonPosition
2173                                         }
2174                                     ]
2175                                     
2176                                 }
2177                                 
2178                                 
2179                             ]
2180                             
2181                         }
2182                     ]
2183                         
2184                 }
2185             ]
2186             
2187             
2188         };
2189           
2190     },
2191     getChildContainer : function() {
2192          
2193          return this.el.select('.modal-body',true).first();
2194         
2195     },
2196     getButtonContainer : function() {
2197          return this.el.select('.modal-footer div',true).first();
2198         
2199     },
2200     initEvents : function()
2201     {
2202         this.el.select('.modal-header .close').on('click', this.hide, this);
2203 //        
2204 //        this.addxtype(this);
2205     },
2206     show : function() {
2207         
2208         if (!this.rendered) {
2209             this.render();
2210         }
2211        
2212         this.el.addClass('on');
2213         this.el.removeClass('fade');
2214         this.el.setStyle('display', 'block');
2215         Roo.get(document.body).addClass("x-body-masked");
2216         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2217         this.maskEl.show();
2218         this.el.setStyle('zIndex', '10001');
2219         this.fireEvent('show', this);
2220         
2221         
2222     },
2223     hide : function()
2224     {
2225         Roo.log('Modal hide?!');
2226         this.maskEl.hide();
2227         Roo.get(document.body).removeClass("x-body-masked");
2228         this.el.removeClass('on');
2229         this.el.addClass('fade');
2230         this.el.setStyle('display', 'none');
2231         this.fireEvent('hide', this);
2232     },
2233     
2234     addButton : function(str, cb)
2235     {
2236          
2237         
2238         var b = Roo.apply({}, { html : str } );
2239         b.xns = b.xns || Roo.bootstrap;
2240         b.xtype = b.xtype || 'Button';
2241         if (typeof(b.listeners) == 'undefined') {
2242             b.listeners = { click : cb.createDelegate(this)  };
2243         }
2244         
2245         var btn = Roo.factory(b);
2246            
2247         btn.onRender(this.el.select('.modal-footer div').first());
2248         
2249         return btn;   
2250        
2251     },
2252     
2253     setDefaultButton : function(btn)
2254     {
2255         //this.el.select('.modal-footer').()
2256     },
2257     resizeTo: function(w,h)
2258     {
2259         // skip..
2260     },
2261     setContentSize  : function(w, h)
2262     {
2263         
2264     },
2265     onButtonClick: function(btn,e)
2266     {
2267         //Roo.log([a,b,c]);
2268         this.fireEvent('btnclick', btn.name, e);
2269     },
2270     setTitle: function(str) {
2271         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2272         
2273     }
2274 });
2275
2276
2277 Roo.apply(Roo.bootstrap.Modal,  {
2278     /**
2279          * Button config that displays a single OK button
2280          * @type Object
2281          */
2282         OK :  [{
2283             name : 'ok',
2284             weight : 'primary',
2285             html : 'OK'
2286         }], 
2287         /**
2288          * Button config that displays Yes and No buttons
2289          * @type Object
2290          */
2291         YESNO : [
2292             {
2293                 name  : 'no',
2294                 html : 'No'
2295             },
2296             {
2297                 name  :'yes',
2298                 weight : 'primary',
2299                 html : 'Yes'
2300             }
2301         ],
2302         
2303         /**
2304          * Button config that displays OK and Cancel buttons
2305          * @type Object
2306          */
2307         OKCANCEL : [
2308             {
2309                name : 'cancel',
2310                 html : 'Cancel'
2311             },
2312             {
2313                 name : 'ok',
2314                 weight : 'primary',
2315                 html : 'OK'
2316             }
2317         ],
2318         /**
2319          * Button config that displays Yes, No and Cancel buttons
2320          * @type Object
2321          */
2322         YESNOCANCEL : [
2323             {
2324                 name : 'yes',
2325                 weight : 'primary',
2326                 html : 'Yes'
2327             },
2328             {
2329                 name : 'no',
2330                 html : 'No'
2331             },
2332             {
2333                 name : 'cancel',
2334                 html : 'Cancel'
2335             }
2336         ]
2337 });
2338  /*
2339  * - LGPL
2340  *
2341  * messagebox - can be used as a replace
2342  * 
2343  */
2344 /**
2345  * @class Roo.MessageBox
2346  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2347  * Example usage:
2348  *<pre><code>
2349 // Basic alert:
2350 Roo.Msg.alert('Status', 'Changes saved successfully.');
2351
2352 // Prompt for user data:
2353 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2354     if (btn == 'ok'){
2355         // process text value...
2356     }
2357 });
2358
2359 // Show a dialog using config options:
2360 Roo.Msg.show({
2361    title:'Save Changes?',
2362    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2363    buttons: Roo.Msg.YESNOCANCEL,
2364    fn: processResult,
2365    animEl: 'elId'
2366 });
2367 </code></pre>
2368  * @singleton
2369  */
2370 Roo.bootstrap.MessageBox = function(){
2371     var dlg, opt, mask, waitTimer;
2372     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2373     var buttons, activeTextEl, bwidth;
2374
2375     
2376     // private
2377     var handleButton = function(button){
2378         dlg.hide();
2379         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2380     };
2381
2382     // private
2383     var handleHide = function(){
2384         if(opt && opt.cls){
2385             dlg.el.removeClass(opt.cls);
2386         }
2387         //if(waitTimer){
2388         //    Roo.TaskMgr.stop(waitTimer);
2389         //    waitTimer = null;
2390         //}
2391     };
2392
2393     // private
2394     var updateButtons = function(b){
2395         var width = 0;
2396         if(!b){
2397             buttons["ok"].hide();
2398             buttons["cancel"].hide();
2399             buttons["yes"].hide();
2400             buttons["no"].hide();
2401             //dlg.footer.dom.style.display = 'none';
2402             return width;
2403         }
2404         dlg.footer.dom.style.display = '';
2405         for(var k in buttons){
2406             if(typeof buttons[k] != "function"){
2407                 if(b[k]){
2408                     buttons[k].show();
2409                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2410                     width += buttons[k].el.getWidth()+15;
2411                 }else{
2412                     buttons[k].hide();
2413                 }
2414             }
2415         }
2416         return width;
2417     };
2418
2419     // private
2420     var handleEsc = function(d, k, e){
2421         if(opt && opt.closable !== false){
2422             dlg.hide();
2423         }
2424         if(e){
2425             e.stopEvent();
2426         }
2427     };
2428
2429     return {
2430         /**
2431          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2432          * @return {Roo.BasicDialog} The BasicDialog element
2433          */
2434         getDialog : function(){
2435            if(!dlg){
2436                 dlg = new Roo.bootstrap.Modal( {
2437                     //draggable: true,
2438                     //resizable:false,
2439                     //constraintoviewport:false,
2440                     //fixedcenter:true,
2441                     //collapsible : false,
2442                     //shim:true,
2443                     //modal: true,
2444                   //  width:400,
2445                   //  height:100,
2446                     //buttonAlign:"center",
2447                     closeClick : function(){
2448                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2449                             handleButton("no");
2450                         }else{
2451                             handleButton("cancel");
2452                         }
2453                     }
2454                 });
2455                 dlg.render();
2456                 dlg.on("hide", handleHide);
2457                 mask = dlg.mask;
2458                 //dlg.addKeyListener(27, handleEsc);
2459                 buttons = {};
2460                 this.buttons = buttons;
2461                 var bt = this.buttonText;
2462                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2463                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2464                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2465                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2466                 Roo.log(buttons)
2467                 bodyEl = dlg.body.createChild({
2468
2469                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2470                         '<textarea class="roo-mb-textarea"></textarea>' +
2471                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2472                 });
2473                 msgEl = bodyEl.dom.firstChild;
2474                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2475                 textboxEl.enableDisplayMode();
2476                 textboxEl.addKeyListener([10,13], function(){
2477                     if(dlg.isVisible() && opt && opt.buttons){
2478                         if(opt.buttons.ok){
2479                             handleButton("ok");
2480                         }else if(opt.buttons.yes){
2481                             handleButton("yes");
2482                         }
2483                     }
2484                 });
2485                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2486                 textareaEl.enableDisplayMode();
2487                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2488                 progressEl.enableDisplayMode();
2489                 var pf = progressEl.dom.firstChild;
2490                 if (pf) {
2491                     pp = Roo.get(pf.firstChild);
2492                     pp.setHeight(pf.offsetHeight);
2493                 }
2494                 
2495             }
2496             return dlg;
2497         },
2498
2499         /**
2500          * Updates the message box body text
2501          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2502          * the XHTML-compliant non-breaking space character '&amp;#160;')
2503          * @return {Roo.MessageBox} This message box
2504          */
2505         updateText : function(text){
2506             if(!dlg.isVisible() && !opt.width){
2507                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2508             }
2509             msgEl.innerHTML = text || '&#160;';
2510       
2511             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2512             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2513             var w = Math.max(
2514                     Math.min(opt.width || cw , this.maxWidth), 
2515                     Math.max(opt.minWidth || this.minWidth, bwidth)
2516             );
2517             if(opt.prompt){
2518                 activeTextEl.setWidth(w);
2519             }
2520             if(dlg.isVisible()){
2521                 dlg.fixedcenter = false;
2522             }
2523             // to big, make it scroll. = But as usual stupid IE does not support
2524             // !important..
2525             
2526             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2527                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2528                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2529             } else {
2530                 bodyEl.dom.style.height = '';
2531                 bodyEl.dom.style.overflowY = '';
2532             }
2533             if (cw > w) {
2534                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2535             } else {
2536                 bodyEl.dom.style.overflowX = '';
2537             }
2538             
2539             dlg.setContentSize(w, bodyEl.getHeight());
2540             if(dlg.isVisible()){
2541                 dlg.fixedcenter = true;
2542             }
2543             return this;
2544         },
2545
2546         /**
2547          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2548          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2549          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2550          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2551          * @return {Roo.MessageBox} This message box
2552          */
2553         updateProgress : function(value, text){
2554             if(text){
2555                 this.updateText(text);
2556             }
2557             if (pp) { // weird bug on my firefox - for some reason this is not defined
2558                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2559             }
2560             return this;
2561         },        
2562
2563         /**
2564          * Returns true if the message box is currently displayed
2565          * @return {Boolean} True if the message box is visible, else false
2566          */
2567         isVisible : function(){
2568             return dlg && dlg.isVisible();  
2569         },
2570
2571         /**
2572          * Hides the message box if it is displayed
2573          */
2574         hide : function(){
2575             if(this.isVisible()){
2576                 dlg.hide();
2577             }  
2578         },
2579
2580         /**
2581          * Displays a new message box, or reinitializes an existing message box, based on the config options
2582          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2583          * The following config object properties are supported:
2584          * <pre>
2585 Property    Type             Description
2586 ----------  ---------------  ------------------------------------------------------------------------------------
2587 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2588                                    closes (defaults to undefined)
2589 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2590                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2591 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2592                                    progress and wait dialogs will ignore this property and always hide the
2593                                    close button as they can only be closed programmatically.
2594 cls               String           A custom CSS class to apply to the message box element
2595 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2596                                    displayed (defaults to 75)
2597 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2598                                    function will be btn (the name of the button that was clicked, if applicable,
2599                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2600                                    Progress and wait dialogs will ignore this option since they do not respond to
2601                                    user actions and can only be closed programmatically, so any required function
2602                                    should be called by the same code after it closes the dialog.
2603 icon              String           A CSS class that provides a background image to be used as an icon for
2604                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2605 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2606 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2607 modal             Boolean          False to allow user interaction with the page while the message box is
2608                                    displayed (defaults to true)
2609 msg               String           A string that will replace the existing message box body text (defaults
2610                                    to the XHTML-compliant non-breaking space character '&#160;')
2611 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2612 progress          Boolean          True to display a progress bar (defaults to false)
2613 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2614 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2615 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2616 title             String           The title text
2617 value             String           The string value to set into the active textbox element if displayed
2618 wait              Boolean          True to display a progress bar (defaults to false)
2619 width             Number           The width of the dialog in pixels
2620 </pre>
2621          *
2622          * Example usage:
2623          * <pre><code>
2624 Roo.Msg.show({
2625    title: 'Address',
2626    msg: 'Please enter your address:',
2627    width: 300,
2628    buttons: Roo.MessageBox.OKCANCEL,
2629    multiline: true,
2630    fn: saveAddress,
2631    animEl: 'addAddressBtn'
2632 });
2633 </code></pre>
2634          * @param {Object} config Configuration options
2635          * @return {Roo.MessageBox} This message box
2636          */
2637         show : function(options)
2638         {
2639             
2640             // this causes nightmares if you show one dialog after another
2641             // especially on callbacks..
2642              
2643             if(this.isVisible()){
2644                 
2645                 this.hide();
2646                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2647                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2648                 Roo.log("New Dialog Message:" +  options.msg )
2649                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2650                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2651                 
2652             }
2653             var d = this.getDialog();
2654             opt = options;
2655             d.setTitle(opt.title || "&#160;");
2656             d.close.setDisplayed(opt.closable !== false);
2657             activeTextEl = textboxEl;
2658             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2659             if(opt.prompt){
2660                 if(opt.multiline){
2661                     textboxEl.hide();
2662                     textareaEl.show();
2663                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2664                         opt.multiline : this.defaultTextHeight);
2665                     activeTextEl = textareaEl;
2666                 }else{
2667                     textboxEl.show();
2668                     textareaEl.hide();
2669                 }
2670             }else{
2671                 textboxEl.hide();
2672                 textareaEl.hide();
2673             }
2674             progressEl.setDisplayed(opt.progress === true);
2675             this.updateProgress(0);
2676             activeTextEl.dom.value = opt.value || "";
2677             if(opt.prompt){
2678                 dlg.setDefaultButton(activeTextEl);
2679             }else{
2680                 var bs = opt.buttons;
2681                 var db = null;
2682                 if(bs && bs.ok){
2683                     db = buttons["ok"];
2684                 }else if(bs && bs.yes){
2685                     db = buttons["yes"];
2686                 }
2687                 dlg.setDefaultButton(db);
2688             }
2689             bwidth = updateButtons(opt.buttons);
2690             this.updateText(opt.msg);
2691             if(opt.cls){
2692                 d.el.addClass(opt.cls);
2693             }
2694             d.proxyDrag = opt.proxyDrag === true;
2695             d.modal = opt.modal !== false;
2696             d.mask = opt.modal !== false ? mask : false;
2697             if(!d.isVisible()){
2698                 // force it to the end of the z-index stack so it gets a cursor in FF
2699                 document.body.appendChild(dlg.el.dom);
2700                 d.animateTarget = null;
2701                 d.show(options.animEl);
2702             }
2703             return this;
2704         },
2705
2706         /**
2707          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2708          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2709          * and closing the message box when the process is complete.
2710          * @param {String} title The title bar text
2711          * @param {String} msg The message box body text
2712          * @return {Roo.MessageBox} This message box
2713          */
2714         progress : function(title, msg){
2715             this.show({
2716                 title : title,
2717                 msg : msg,
2718                 buttons: false,
2719                 progress:true,
2720                 closable:false,
2721                 minWidth: this.minProgressWidth,
2722                 modal : true
2723             });
2724             return this;
2725         },
2726
2727         /**
2728          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2729          * If a callback function is passed it will be called after the user clicks the button, and the
2730          * id of the button that was clicked will be passed as the only parameter to the callback
2731          * (could also be the top-right close button).
2732          * @param {String} title The title bar text
2733          * @param {String} msg The message box body text
2734          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2735          * @param {Object} scope (optional) The scope of the callback function
2736          * @return {Roo.MessageBox} This message box
2737          */
2738         alert : function(title, msg, fn, scope){
2739             this.show({
2740                 title : title,
2741                 msg : msg,
2742                 buttons: this.OK,
2743                 fn: fn,
2744                 scope : scope,
2745                 modal : true
2746             });
2747             return this;
2748         },
2749
2750         /**
2751          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2752          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2753          * You are responsible for closing the message box when the process is complete.
2754          * @param {String} msg The message box body text
2755          * @param {String} title (optional) The title bar text
2756          * @return {Roo.MessageBox} This message box
2757          */
2758         wait : function(msg, title){
2759             this.show({
2760                 title : title,
2761                 msg : msg,
2762                 buttons: false,
2763                 closable:false,
2764                 progress:true,
2765                 modal:true,
2766                 width:300,
2767                 wait:true
2768             });
2769             waitTimer = Roo.TaskMgr.start({
2770                 run: function(i){
2771                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2772                 },
2773                 interval: 1000
2774             });
2775             return this;
2776         },
2777
2778         /**
2779          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2780          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2781          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2782          * @param {String} title The title bar text
2783          * @param {String} msg The message box body text
2784          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2785          * @param {Object} scope (optional) The scope of the callback function
2786          * @return {Roo.MessageBox} This message box
2787          */
2788         confirm : function(title, msg, fn, scope){
2789             this.show({
2790                 title : title,
2791                 msg : msg,
2792                 buttons: this.YESNO,
2793                 fn: fn,
2794                 scope : scope,
2795                 modal : true
2796             });
2797             return this;
2798         },
2799
2800         /**
2801          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2802          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2803          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2804          * (could also be the top-right close button) and the text that was entered will be passed as the two
2805          * parameters to the callback.
2806          * @param {String} title The title bar text
2807          * @param {String} msg The message box body text
2808          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2809          * @param {Object} scope (optional) The scope of the callback function
2810          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2811          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2812          * @return {Roo.MessageBox} This message box
2813          */
2814         prompt : function(title, msg, fn, scope, multiline){
2815             this.show({
2816                 title : title,
2817                 msg : msg,
2818                 buttons: this.OKCANCEL,
2819                 fn: fn,
2820                 minWidth:250,
2821                 scope : scope,
2822                 prompt:true,
2823                 multiline: multiline,
2824                 modal : true
2825             });
2826             return this;
2827         },
2828
2829         /**
2830          * Button config that displays a single OK button
2831          * @type Object
2832          */
2833         OK : {ok:true},
2834         /**
2835          * Button config that displays Yes and No buttons
2836          * @type Object
2837          */
2838         YESNO : {yes:true, no:true},
2839         /**
2840          * Button config that displays OK and Cancel buttons
2841          * @type Object
2842          */
2843         OKCANCEL : {ok:true, cancel:true},
2844         /**
2845          * Button config that displays Yes, No and Cancel buttons
2846          * @type Object
2847          */
2848         YESNOCANCEL : {yes:true, no:true, cancel:true},
2849
2850         /**
2851          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2852          * @type Number
2853          */
2854         defaultTextHeight : 75,
2855         /**
2856          * The maximum width in pixels of the message box (defaults to 600)
2857          * @type Number
2858          */
2859         maxWidth : 600,
2860         /**
2861          * The minimum width in pixels of the message box (defaults to 100)
2862          * @type Number
2863          */
2864         minWidth : 100,
2865         /**
2866          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2867          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2868          * @type Number
2869          */
2870         minProgressWidth : 250,
2871         /**
2872          * An object containing the default button text strings that can be overriden for localized language support.
2873          * Supported properties are: ok, cancel, yes and no.
2874          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2875          * @type Object
2876          */
2877         buttonText : {
2878             ok : "OK",
2879             cancel : "Cancel",
2880             yes : "Yes",
2881             no : "No"
2882         }
2883     };
2884 }();
2885
2886 /**
2887  * Shorthand for {@link Roo.MessageBox}
2888  */
2889 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2890 Roo.Msg = Roo.Msg || Roo.MessageBox;
2891 /*
2892  * - LGPL
2893  *
2894  * navbar
2895  * 
2896  */
2897
2898 /**
2899  * @class Roo.bootstrap.Navbar
2900  * @extends Roo.bootstrap.Component
2901  * Bootstrap Navbar class
2902
2903  * @constructor
2904  * Create a new Navbar
2905  * @param {Object} config The config object
2906  */
2907
2908
2909 Roo.bootstrap.Navbar = function(config){
2910     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2911     
2912 };
2913
2914 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2915     
2916     
2917    
2918     // private
2919     navItems : false,
2920     loadMask : false,
2921     
2922     
2923     getAutoCreate : function(){
2924         
2925         
2926         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2927         
2928     },
2929     
2930     initEvents :function ()
2931     {
2932         //Roo.log(this.el.select('.navbar-toggle',true));
2933         this.el.select('.navbar-toggle',true).on('click', function() {
2934            // Roo.log('click');
2935             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2936         }, this);
2937         
2938         var mark = {
2939             tag: "div",
2940             cls:"x-dlg-mask"
2941         }
2942         
2943         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2944         
2945         var size = this.el.getSize();
2946         this.maskEl.setSize(size.width, size.height);
2947         this.maskEl.enableDisplayMode("block");
2948         this.maskEl.hide();
2949         
2950         if(this.loadMask){
2951             this.maskEl.show();
2952         }
2953     },
2954     
2955     
2956     getChildContainer : function()
2957     {
2958         if (this.el.select('.collapse').getCount()) {
2959             return this.el.select('.collapse',true).first();
2960         }
2961         
2962         return this.el;
2963     },
2964     
2965     mask : function()
2966     {
2967         this.maskEl.show();
2968     },
2969     
2970     unmask : function()
2971     {
2972         this.maskEl.hide();
2973     } 
2974     
2975     
2976     
2977     
2978 });
2979
2980
2981
2982  
2983
2984  /*
2985  * - LGPL
2986  *
2987  * navbar
2988  * 
2989  */
2990
2991 /**
2992  * @class Roo.bootstrap.NavSimplebar
2993  * @extends Roo.bootstrap.Navbar
2994  * Bootstrap Sidebar class
2995  *
2996  * @cfg {Boolean} inverse is inverted color
2997  * 
2998  * @cfg {String} type (nav | pills | tabs)
2999  * @cfg {Boolean} arrangement stacked | justified
3000  * @cfg {String} align (left | right) alignment
3001  * 
3002  * @cfg {Boolean} main (true|false) main nav bar? default false
3003  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3004  * 
3005  * @cfg {String} tag (header|footer|nav|div) default is nav 
3006
3007  * 
3008  * 
3009  * 
3010  * @constructor
3011  * Create a new Sidebar
3012  * @param {Object} config The config object
3013  */
3014
3015
3016 Roo.bootstrap.NavSimplebar = function(config){
3017     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3018 };
3019
3020 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3021     
3022     inverse: false,
3023     
3024     type: false,
3025     arrangement: '',
3026     align : false,
3027     
3028     
3029     
3030     main : false,
3031     
3032     
3033     tag : false,
3034     
3035     
3036     getAutoCreate : function(){
3037         
3038         
3039         var cfg = {
3040             tag : this.tag || 'div',
3041             cls : 'navbar'
3042         };
3043           
3044         
3045         cfg.cn = [
3046             {
3047                 cls: 'nav',
3048                 tag : 'ul'
3049             }
3050         ];
3051         
3052          
3053         this.type = this.type || 'nav';
3054         if (['tabs','pills'].indexOf(this.type)!==-1) {
3055             cfg.cn[0].cls += ' nav-' + this.type
3056         
3057         
3058         } else {
3059             if (this.type!=='nav') {
3060                 Roo.log('nav type must be nav/tabs/pills')
3061             }
3062             cfg.cn[0].cls += ' navbar-nav'
3063         }
3064         
3065         
3066         
3067         
3068         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3069             cfg.cn[0].cls += ' nav-' + this.arrangement;
3070         }
3071         
3072         
3073         if (this.align === 'right') {
3074             cfg.cn[0].cls += ' navbar-right';
3075         }
3076         
3077         if (this.inverse) {
3078             cfg.cls += ' navbar-inverse';
3079             
3080         }
3081         
3082         
3083         return cfg;
3084     
3085         
3086     }
3087     
3088     
3089     
3090 });
3091
3092
3093
3094  
3095
3096  
3097        /*
3098  * - LGPL
3099  *
3100  * navbar
3101  * 
3102  */
3103
3104 /**
3105  * @class Roo.bootstrap.NavHeaderbar
3106  * @extends Roo.bootstrap.NavSimplebar
3107  * Bootstrap Sidebar class
3108  *
3109  * @cfg {String} brand what is brand
3110  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3111  * @cfg {String} brand_href href of the brand
3112  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3113  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3114  * 
3115  * @constructor
3116  * Create a new Sidebar
3117  * @param {Object} config The config object
3118  */
3119
3120
3121 Roo.bootstrap.NavHeaderbar = function(config){
3122     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3123 };
3124
3125 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3126     
3127     position: '',
3128     brand: '',
3129     brand_href: false,
3130     srButton : true,
3131     autohide : false,
3132     
3133     getAutoCreate : function(){
3134         
3135         var   cfg = {
3136             tag: this.nav || 'nav',
3137             cls: 'navbar',
3138             role: 'navigation',
3139             cn: []
3140         };
3141         
3142         if(this.srButton){
3143             cfg.cn.push({
3144                 tag: 'div',
3145                 cls: 'navbar-header',
3146                 cn: [
3147                     {
3148                         tag: 'button',
3149                         type: 'button',
3150                         cls: 'navbar-toggle',
3151                         'data-toggle': 'collapse',
3152                         cn: [
3153                             {
3154                                 tag: 'span',
3155                                 cls: 'sr-only',
3156                                 html: 'Toggle navigation'
3157                             },
3158                             {
3159                                 tag: 'span',
3160                                 cls: 'icon-bar'
3161                             },
3162                             {
3163                                 tag: 'span',
3164                                 cls: 'icon-bar'
3165                             },
3166                             {
3167                                 tag: 'span',
3168                                 cls: 'icon-bar'
3169                             }
3170                         ]
3171                     }
3172                 ]
3173             });
3174         }
3175         
3176         cfg.cn.push({
3177             tag: 'div',
3178             cls: 'collapse navbar-collapse',
3179             cn : []
3180         });
3181         
3182         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3183         
3184         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3185             cfg.cls += ' navbar-' + this.position;
3186             
3187             // tag can override this..
3188             
3189             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3190         }
3191         
3192         if (this.brand !== '') {
3193             cfg.cn[0].cn.push({
3194                 tag: 'a',
3195                 href: this.brand_href ? this.brand_href : '#',
3196                 cls: 'navbar-brand',
3197                 cn: [
3198                 this.brand
3199                 ]
3200             });
3201         }
3202         
3203         if(this.main){
3204             cfg.cls += ' main-nav';
3205         }
3206         
3207         
3208         return cfg;
3209
3210         
3211     },
3212     initEvents : function()
3213     {
3214         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3215         
3216         if (this.autohide) {
3217             
3218             var prevScroll = 0;
3219             var ft = this.el;
3220             
3221             Roo.get(document).on('scroll',function(e) {
3222                 var ns = Roo.get(document).getScroll().top;
3223                 var os = prevScroll;
3224                 prevScroll = ns;
3225                 
3226                 if(ns > os){
3227                     ft.removeClass('slideDown');
3228                     ft.addClass('slideUp');
3229                     return;
3230                 }
3231                 ft.removeClass('slideUp');
3232                 ft.addClass('slideDown');
3233                  
3234               
3235           },this);
3236         }
3237     }    
3238           
3239       
3240     
3241     
3242 });
3243
3244
3245
3246  
3247
3248  /*
3249  * - LGPL
3250  *
3251  * navbar
3252  * 
3253  */
3254
3255 /**
3256  * @class Roo.bootstrap.NavSidebar
3257  * @extends Roo.bootstrap.Navbar
3258  * Bootstrap Sidebar class
3259  * 
3260  * @constructor
3261  * Create a new Sidebar
3262  * @param {Object} config The config object
3263  */
3264
3265
3266 Roo.bootstrap.NavSidebar = function(config){
3267     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3268 };
3269
3270 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3271     
3272     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3273     
3274     getAutoCreate : function(){
3275         
3276         
3277         return  {
3278             tag: 'div',
3279             cls: 'sidebar sidebar-nav'
3280         };
3281     
3282         
3283     }
3284     
3285     
3286     
3287 });
3288
3289
3290
3291  
3292
3293  /*
3294  * - LGPL
3295  *
3296  * nav group
3297  * 
3298  */
3299
3300 /**
3301  * @class Roo.bootstrap.NavGroup
3302  * @extends Roo.bootstrap.Component
3303  * Bootstrap NavGroup class
3304  * @cfg {String} align left | right
3305  * @cfg {Boolean} inverse false | true
3306  * @cfg {String} type (nav|pills|tab) default nav
3307  * @cfg {String} navId - reference Id for navbar.
3308
3309  * 
3310  * @constructor
3311  * Create a new nav group
3312  * @param {Object} config The config object
3313  */
3314
3315 Roo.bootstrap.NavGroup = function(config){
3316     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3317     this.navItems = [];
3318    
3319     Roo.bootstrap.NavGroup.register(this);
3320      this.addEvents({
3321         /**
3322              * @event changed
3323              * Fires when the active item changes
3324              * @param {Roo.bootstrap.NavGroup} this
3325              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3326              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3327          */
3328         'changed': true
3329      });
3330     
3331 };
3332
3333 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3334     
3335     align: '',
3336     inverse: false,
3337     form: false,
3338     type: 'nav',
3339     navId : '',
3340     // private
3341     
3342     navItems : false, 
3343     
3344     getAutoCreate : function()
3345     {
3346         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3347         
3348         cfg = {
3349             tag : 'ul',
3350             cls: 'nav' 
3351         }
3352         
3353         if (['tabs','pills'].indexOf(this.type)!==-1) {
3354             cfg.cls += ' nav-' + this.type
3355         } else {
3356             if (this.type!=='nav') {
3357                 Roo.log('nav type must be nav/tabs/pills')
3358             }
3359             cfg.cls += ' navbar-nav'
3360         }
3361         
3362         if (this.parent().sidebar) {
3363             cfg = {
3364                 tag: 'ul',
3365                 cls: 'dashboard-menu sidebar-menu'
3366             }
3367             
3368             return cfg;
3369         }
3370         
3371         if (this.form === true) {
3372             cfg = {
3373                 tag: 'form',
3374                 cls: 'navbar-form'
3375             }
3376             
3377             if (this.align === 'right') {
3378                 cfg.cls += ' navbar-right';
3379             } else {
3380                 cfg.cls += ' navbar-left';
3381             }
3382         }
3383         
3384         if (this.align === 'right') {
3385             cfg.cls += ' navbar-right';
3386         }
3387         
3388         if (this.inverse) {
3389             cfg.cls += ' navbar-inverse';
3390             
3391         }
3392         
3393         
3394         return cfg;
3395     },
3396     /**
3397     * sets the active Navigation item
3398     * @param {Roo.bootstrap.NavItem} the new current navitem
3399     */
3400     setActiveItem : function(item)
3401     {
3402         var prev = false;
3403         Roo.each(this.navItems, function(v){
3404             if (v == item) {
3405                 return ;
3406             }
3407             if (v.isActive()) {
3408                 v.setActive(false, true);
3409                 prev = v;
3410                 
3411             }
3412             
3413         });
3414
3415         item.setActive(true, true);
3416         this.fireEvent('changed', this, item, prev);
3417         
3418         
3419     },
3420     /**
3421     * gets the active Navigation item
3422     * @return {Roo.bootstrap.NavItem} the current navitem
3423     */
3424     getActive : function()
3425     {
3426         
3427         var prev = false;
3428         Roo.each(this.navItems, function(v){
3429             
3430             if (v.isActive()) {
3431                 prev = v;
3432                 
3433             }
3434             
3435         });
3436         return prev;
3437     },
3438     
3439     indexOfNav : function()
3440     {
3441         
3442         var prev = false;
3443         Roo.each(this.navItems, function(v,i){
3444             
3445             if (v.isActive()) {
3446                 prev = i;
3447                 
3448             }
3449             
3450         });
3451         return prev;
3452     },
3453     /**
3454     * adds a Navigation item
3455     * @param {Roo.bootstrap.NavItem} the navitem to add
3456     */
3457     addItem : function(cfg)
3458     {
3459         var cn = new Roo.bootstrap.NavItem(cfg);
3460         this.register(cn);
3461         cn.parentId = this.id;
3462         cn.onRender(this.el, null);
3463         return cn;
3464     },
3465     /**
3466     * register a Navigation item
3467     * @param {Roo.bootstrap.NavItem} the navitem to add
3468     */
3469     register : function(item)
3470     {
3471         this.navItems.push( item);
3472         item.navId = this.navId;
3473     
3474     },
3475   
3476     
3477     getNavItem: function(tabId)
3478     {
3479         var ret = false;
3480         Roo.each(this.navItems, function(e) {
3481             if (e.tabId == tabId) {
3482                ret =  e;
3483                return false;
3484             }
3485             return true;
3486             
3487         });
3488         return ret;
3489     },
3490     
3491     setActiveNext : function()
3492     {
3493         var i = this.indexOfNav(this.getActive());
3494         if (i > this.navItems.length) {
3495             return;
3496         }
3497         this.setActiveItem(this.navItems[i+1]);
3498     },
3499     setActivePrev : function()
3500     {
3501         var i = this.indexOfNav(this.getActive());
3502         if (i  < 1) {
3503             return;
3504         }
3505         this.setActiveItem(this.navItems[i-1]);
3506     },
3507     clearWasActive : function(except) {
3508         Roo.each(this.navItems, function(e) {
3509             if (e.tabId != except.tabId && e.was_active) {
3510                e.was_active = false;
3511                return false;
3512             }
3513             return true;
3514             
3515         });
3516     },
3517     getWasActive : function ()
3518     {
3519         var r = false;
3520         Roo.each(this.navItems, function(e) {
3521             if (e.was_active) {
3522                r = e;
3523                return false;
3524             }
3525             return true;
3526             
3527         });
3528         return r;
3529     }
3530     
3531     
3532 });
3533
3534  
3535 Roo.apply(Roo.bootstrap.NavGroup, {
3536     
3537     groups: {},
3538      /**
3539     * register a Navigation Group
3540     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3541     */
3542     register : function(navgrp)
3543     {
3544         this.groups[navgrp.navId] = navgrp;
3545         
3546     },
3547     /**
3548     * fetch a Navigation Group based on the navigation ID
3549     * @param {string} the navgroup to add
3550     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3551     */
3552     get: function(navId) {
3553         if (typeof(this.groups[navId]) == 'undefined') {
3554             return false;
3555             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3556         }
3557         return this.groups[navId] ;
3558     }
3559     
3560     
3561     
3562 });
3563
3564  /*
3565  * - LGPL
3566  *
3567  * row
3568  * 
3569  */
3570
3571 /**
3572  * @class Roo.bootstrap.NavItem
3573  * @extends Roo.bootstrap.Component
3574  * Bootstrap Navbar.NavItem class
3575  * @cfg {String} href  link to
3576  * @cfg {String} html content of button
3577  * @cfg {String} badge text inside badge
3578  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3579  * @cfg {String} glyphicon name of glyphicon
3580  * @cfg {String} icon name of font awesome icon
3581  * @cfg {Boolean} active Is item active
3582  * @cfg {Boolean} disabled Is item disabled
3583  
3584  * @cfg {Boolean} preventDefault (true | false) default false
3585  * @cfg {String} tabId the tab that this item activates.
3586  * @cfg {String} tagtype (a|span) render as a href or span?
3587   
3588  * @constructor
3589  * Create a new Navbar Item
3590  * @param {Object} config The config object
3591  */
3592 Roo.bootstrap.NavItem = function(config){
3593     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3594     this.addEvents({
3595         // raw events
3596         /**
3597          * @event click
3598          * The raw click event for the entire grid.
3599          * @param {Roo.EventObject} e
3600          */
3601         "click" : true,
3602          /**
3603             * @event changed
3604             * Fires when the active item active state changes
3605             * @param {Roo.bootstrap.NavItem} this
3606             * @param {boolean} state the new state
3607              
3608          */
3609         'changed': true
3610     });
3611    
3612 };
3613
3614 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3615     
3616     href: false,
3617     html: '',
3618     badge: '',
3619     icon: false,
3620     glyphicon: false,
3621     active: false,
3622     preventDefault : false,
3623     tabId : false,
3624     tagtype : 'a',
3625     disabled : false,
3626     
3627     was_active : false,
3628     
3629     getAutoCreate : function(){
3630          
3631         var cfg = {
3632             tag: 'li',
3633             cls: 'nav-item'
3634             
3635         }
3636         if (this.active) {
3637             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3638         }
3639         if (this.disabled) {
3640             cfg.cls += ' disabled';
3641         }
3642         
3643         if (this.href || this.html || this.glyphicon || this.icon) {
3644             cfg.cn = [
3645                 {
3646                     tag: this.tagtype,
3647                     href : this.href || "#",
3648                     html: this.html || ''
3649                 }
3650             ];
3651             
3652             if (this.icon) {
3653                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3654             }
3655
3656             if(this.glyphicon) {
3657                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3658             }
3659             
3660             if (this.menu) {
3661                 
3662                 cfg.cn[0].html += " <span class='caret'></span>";
3663              
3664             }
3665             
3666             if (this.badge !== '') {
3667                  
3668                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3669             }
3670         }
3671         
3672         
3673         
3674         return cfg;
3675     },
3676     initEvents: function() {
3677        // Roo.log('init events?');
3678        // Roo.log(this.el.dom);
3679         if (typeof (this.menu) != 'undefined') {
3680             this.menu.parentType = this.xtype;
3681             this.menu.triggerEl = this.el;
3682             this.addxtype(Roo.apply({}, this.menu));
3683         }
3684
3685        
3686         this.el.select('a',true).on('click', this.onClick, this);
3687         // at this point parent should be available..
3688         this.parent().register(this);
3689     },
3690     
3691     onClick : function(e)
3692     {
3693          
3694         if(this.preventDefault){
3695             e.preventDefault();
3696         }
3697         if (this.disabled) {
3698             return;
3699         }
3700         
3701         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3702         if (tg && tg.transition) {
3703             Roo.log("waiting for the transitionend");
3704             return;
3705         }
3706         
3707         Roo.log("fire event clicked");
3708         if(this.fireEvent('click', this, e) === false){
3709             return;
3710         };
3711         var p = this.parent();
3712         if (['tabs','pills'].indexOf(p.type)!==-1) {
3713             if (typeof(p.setActiveItem) !== 'undefined') {
3714                 p.setActiveItem(this);
3715             }
3716         }
3717         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3718         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3719             // remove the collapsed menu expand...
3720             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3721         }
3722         
3723     },
3724     
3725     isActive: function () {
3726         return this.active
3727     },
3728     setActive : function(state, fire, is_was_active)
3729     {
3730         if (this.active && !state & this.navId) {
3731             this.was_active = true;
3732             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3733             if (nv) {
3734                 nv.clearWasActive(this);
3735             }
3736             
3737         }
3738         this.active = state;
3739         
3740         if (!state ) {
3741             this.el.removeClass('active');
3742         } else if (!this.el.hasClass('active')) {
3743             this.el.addClass('active');
3744         }
3745         if (fire) {
3746             this.fireEvent('changed', this, state);
3747         }
3748         
3749         // show a panel if it's registered and related..
3750         
3751         if (!this.navId || !this.tabId || !state || is_was_active) {
3752             return;
3753         }
3754         
3755         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3756         if (!tg) {
3757             return;
3758         }
3759         var pan = tg.getPanelByName(this.tabId);
3760         if (!pan) {
3761             return;
3762         }
3763         // if we can not flip to new panel - go back to old nav highlight..
3764         if (false == tg.showPanel(pan)) {
3765             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3766             if (nv) {
3767                 var onav = nv.getWasActive();
3768                 if (onav) {
3769                     onav.setActive(true, false, true);
3770                 }
3771             }
3772             
3773         }
3774         
3775         
3776         
3777     },
3778      // this should not be here...
3779     setDisabled : function(state)
3780     {
3781         this.disabled = state;
3782         if (!state ) {
3783             this.el.removeClass('disabled');
3784         } else if (!this.el.hasClass('disabled')) {
3785             this.el.addClass('disabled');
3786         }
3787         
3788     }
3789 });
3790  
3791
3792  /*
3793  * - LGPL
3794  *
3795  * sidebar item
3796  *
3797  *  li
3798  *    <span> icon </span>
3799  *    <span> text </span>
3800  *    <span>badge </span>
3801  */
3802
3803 /**
3804  * @class Roo.bootstrap.NavSidebarItem
3805  * @extends Roo.bootstrap.NavItem
3806  * Bootstrap Navbar.NavSidebarItem class
3807  * @constructor
3808  * Create a new Navbar Button
3809  * @param {Object} config The config object
3810  */
3811 Roo.bootstrap.NavSidebarItem = function(config){
3812     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3813     this.addEvents({
3814         // raw events
3815         /**
3816          * @event click
3817          * The raw click event for the entire grid.
3818          * @param {Roo.EventObject} e
3819          */
3820         "click" : true,
3821          /**
3822             * @event changed
3823             * Fires when the active item active state changes
3824             * @param {Roo.bootstrap.NavSidebarItem} this
3825             * @param {boolean} state the new state
3826              
3827          */
3828         'changed': true
3829     });
3830    
3831 };
3832
3833 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3834     
3835     
3836     getAutoCreate : function(){
3837         
3838         
3839         var a = {
3840                 tag: 'a',
3841                 href : this.href || '#',
3842                 cls: '',
3843                 html : '',
3844                 cn : []
3845         };
3846         var cfg = {
3847             tag: 'li',
3848             cls: '',
3849             cn: [ a ]
3850         }
3851         var span = {
3852             tag: 'span',
3853             html : this.html || ''
3854         }
3855         
3856         
3857         if (this.active) {
3858             cfg.cls += ' active';
3859         }
3860         
3861         // left icon..
3862         if (this.glyphicon || this.icon) {
3863             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3864             a.cn.push({ tag : 'i', cls : c }) ;
3865         }
3866         // html..
3867         a.cn.push(span);
3868         // then badge..
3869         if (this.badge !== '') {
3870             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3871         }
3872         // fi
3873         if (this.menu) {
3874             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3875             a.cls += 'dropdown-toggle treeview' ;
3876             
3877         }
3878         
3879         
3880         
3881         return cfg;
3882          
3883            
3884     }
3885    
3886      
3887  
3888 });
3889  
3890
3891  /*
3892  * - LGPL
3893  *
3894  * row
3895  * 
3896  */
3897
3898 /**
3899  * @class Roo.bootstrap.Row
3900  * @extends Roo.bootstrap.Component
3901  * Bootstrap Row class (contains columns...)
3902  * 
3903  * @constructor
3904  * Create a new Row
3905  * @param {Object} config The config object
3906  */
3907
3908 Roo.bootstrap.Row = function(config){
3909     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3910 };
3911
3912 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3913     
3914     getAutoCreate : function(){
3915        return {
3916             cls: 'row clearfix'
3917        };
3918     }
3919     
3920     
3921 });
3922
3923  
3924
3925  /*
3926  * - LGPL
3927  *
3928  * element
3929  * 
3930  */
3931
3932 /**
3933  * @class Roo.bootstrap.Element
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap Element class
3936  * @cfg {String} html contents of the element
3937  * @cfg {String} tag tag of the element
3938  * @cfg {String} cls class of the element
3939  * 
3940  * @constructor
3941  * Create a new Element
3942  * @param {Object} config The config object
3943  */
3944
3945 Roo.bootstrap.Element = function(config){
3946     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3947 };
3948
3949 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3950     
3951     tag: 'div',
3952     cls: '',
3953     html: '',
3954      
3955     
3956     getAutoCreate : function(){
3957         
3958         var cfg = {
3959             tag: this.tag,
3960             cls: this.cls,
3961             html: this.html
3962         }
3963         
3964         
3965         
3966         return cfg;
3967     }
3968    
3969 });
3970
3971  
3972
3973  /*
3974  * - LGPL
3975  *
3976  * pagination
3977  * 
3978  */
3979
3980 /**
3981  * @class Roo.bootstrap.Pagination
3982  * @extends Roo.bootstrap.Component
3983  * Bootstrap Pagination class
3984  * @cfg {String} size xs | sm | md | lg
3985  * @cfg {Boolean} inverse false | true
3986  * 
3987  * @constructor
3988  * Create a new Pagination
3989  * @param {Object} config The config object
3990  */
3991
3992 Roo.bootstrap.Pagination = function(config){
3993     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3994 };
3995
3996 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3997     
3998     cls: false,
3999     size: false,
4000     inverse: false,
4001     
4002     getAutoCreate : function(){
4003         var cfg = {
4004             tag: 'ul',
4005                 cls: 'pagination'
4006         };
4007         if (this.inverse) {
4008             cfg.cls += ' inverse';
4009         }
4010         if (this.html) {
4011             cfg.html=this.html;
4012         }
4013         if (this.cls) {
4014             cfg.cls += " " + this.cls;
4015         }
4016         return cfg;
4017     }
4018    
4019 });
4020
4021  
4022
4023  /*
4024  * - LGPL
4025  *
4026  * Pagination item
4027  * 
4028  */
4029
4030
4031 /**
4032  * @class Roo.bootstrap.PaginationItem
4033  * @extends Roo.bootstrap.Component
4034  * Bootstrap PaginationItem class
4035  * @cfg {String} html text
4036  * @cfg {String} href the link
4037  * @cfg {Boolean} preventDefault (true | false) default true
4038  * @cfg {Boolean} active (true | false) default false
4039  * 
4040  * 
4041  * @constructor
4042  * Create a new PaginationItem
4043  * @param {Object} config The config object
4044  */
4045
4046
4047 Roo.bootstrap.PaginationItem = function(config){
4048     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4049     this.addEvents({
4050         // raw events
4051         /**
4052          * @event click
4053          * The raw click event for the entire grid.
4054          * @param {Roo.EventObject} e
4055          */
4056         "click" : true
4057     });
4058 };
4059
4060 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4061     
4062     href : false,
4063     html : false,
4064     preventDefault: true,
4065     active : false,
4066     cls : false,
4067     
4068     getAutoCreate : function(){
4069         var cfg= {
4070             tag: 'li',
4071             cn: [
4072                 {
4073                     tag : 'a',
4074                     href : this.href ? this.href : '#',
4075                     html : this.html ? this.html : ''
4076                 }
4077             ]
4078         };
4079         
4080         if(this.cls){
4081             cfg.cls = this.cls;
4082         }
4083         
4084         if(this.active){
4085             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4086         }
4087         
4088         return cfg;
4089     },
4090     
4091     initEvents: function() {
4092         
4093         this.el.on('click', this.onClick, this);
4094         
4095     },
4096     onClick : function(e)
4097     {
4098         Roo.log('PaginationItem on click ');
4099         if(this.preventDefault){
4100             e.preventDefault();
4101         }
4102         
4103         this.fireEvent('click', this, e);
4104     }
4105    
4106 });
4107
4108  
4109
4110  /*
4111  * - LGPL
4112  *
4113  * slider
4114  * 
4115  */
4116
4117
4118 /**
4119  * @class Roo.bootstrap.Slider
4120  * @extends Roo.bootstrap.Component
4121  * Bootstrap Slider class
4122  *    
4123  * @constructor
4124  * Create a new Slider
4125  * @param {Object} config The config object
4126  */
4127
4128 Roo.bootstrap.Slider = function(config){
4129     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4130 };
4131
4132 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4133     
4134     getAutoCreate : function(){
4135         
4136         var cfg = {
4137             tag: 'div',
4138             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4139             cn: [
4140                 {
4141                     tag: 'a',
4142                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4143                 }
4144             ]
4145         }
4146         
4147         return cfg;
4148     }
4149    
4150 });
4151
4152  /*
4153  * Based on:
4154  * Ext JS Library 1.1.1
4155  * Copyright(c) 2006-2007, Ext JS, LLC.
4156  *
4157  * Originally Released Under LGPL - original licence link has changed is not relivant.
4158  *
4159  * Fork - LGPL
4160  * <script type="text/javascript">
4161  */
4162  
4163
4164 /**
4165  * @class Roo.grid.ColumnModel
4166  * @extends Roo.util.Observable
4167  * This is the default implementation of a ColumnModel used by the Grid. It defines
4168  * the columns in the grid.
4169  * <br>Usage:<br>
4170  <pre><code>
4171  var colModel = new Roo.grid.ColumnModel([
4172         {header: "Ticker", width: 60, sortable: true, locked: true},
4173         {header: "Company Name", width: 150, sortable: true},
4174         {header: "Market Cap.", width: 100, sortable: true},
4175         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4176         {header: "Employees", width: 100, sortable: true, resizable: false}
4177  ]);
4178  </code></pre>
4179  * <p>
4180  
4181  * The config options listed for this class are options which may appear in each
4182  * individual column definition.
4183  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4184  * @constructor
4185  * @param {Object} config An Array of column config objects. See this class's
4186  * config objects for details.
4187 */
4188 Roo.grid.ColumnModel = function(config){
4189         /**
4190      * The config passed into the constructor
4191      */
4192     this.config = config;
4193     this.lookup = {};
4194
4195     // if no id, create one
4196     // if the column does not have a dataIndex mapping,
4197     // map it to the order it is in the config
4198     for(var i = 0, len = config.length; i < len; i++){
4199         var c = config[i];
4200         if(typeof c.dataIndex == "undefined"){
4201             c.dataIndex = i;
4202         }
4203         if(typeof c.renderer == "string"){
4204             c.renderer = Roo.util.Format[c.renderer];
4205         }
4206         if(typeof c.id == "undefined"){
4207             c.id = Roo.id();
4208         }
4209         if(c.editor && c.editor.xtype){
4210             c.editor  = Roo.factory(c.editor, Roo.grid);
4211         }
4212         if(c.editor && c.editor.isFormField){
4213             c.editor = new Roo.grid.GridEditor(c.editor);
4214         }
4215         this.lookup[c.id] = c;
4216     }
4217
4218     /**
4219      * The width of columns which have no width specified (defaults to 100)
4220      * @type Number
4221      */
4222     this.defaultWidth = 100;
4223
4224     /**
4225      * Default sortable of columns which have no sortable specified (defaults to false)
4226      * @type Boolean
4227      */
4228     this.defaultSortable = false;
4229
4230     this.addEvents({
4231         /**
4232              * @event widthchange
4233              * Fires when the width of a column changes.
4234              * @param {ColumnModel} this
4235              * @param {Number} columnIndex The column index
4236              * @param {Number} newWidth The new width
4237              */
4238             "widthchange": true,
4239         /**
4240              * @event headerchange
4241              * Fires when the text of a header changes.
4242              * @param {ColumnModel} this
4243              * @param {Number} columnIndex The column index
4244              * @param {Number} newText The new header text
4245              */
4246             "headerchange": true,
4247         /**
4248              * @event hiddenchange
4249              * Fires when a column is hidden or "unhidden".
4250              * @param {ColumnModel} this
4251              * @param {Number} columnIndex The column index
4252              * @param {Boolean} hidden true if hidden, false otherwise
4253              */
4254             "hiddenchange": true,
4255             /**
4256          * @event columnmoved
4257          * Fires when a column is moved.
4258          * @param {ColumnModel} this
4259          * @param {Number} oldIndex
4260          * @param {Number} newIndex
4261          */
4262         "columnmoved" : true,
4263         /**
4264          * @event columlockchange
4265          * Fires when a column's locked state is changed
4266          * @param {ColumnModel} this
4267          * @param {Number} colIndex
4268          * @param {Boolean} locked true if locked
4269          */
4270         "columnlockchange" : true
4271     });
4272     Roo.grid.ColumnModel.superclass.constructor.call(this);
4273 };
4274 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4275     /**
4276      * @cfg {String} header The header text to display in the Grid view.
4277      */
4278     /**
4279      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4280      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4281      * specified, the column's index is used as an index into the Record's data Array.
4282      */
4283     /**
4284      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4285      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4286      */
4287     /**
4288      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4289      * Defaults to the value of the {@link #defaultSortable} property.
4290      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4291      */
4292     /**
4293      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4294      */
4295     /**
4296      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4297      */
4298     /**
4299      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4300      */
4301     /**
4302      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4303      */
4304     /**
4305      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4306      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4307      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4308      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4309      */
4310        /**
4311      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4312      */
4313     /**
4314      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4315      */
4316
4317     /**
4318      * Returns the id of the column at the specified index.
4319      * @param {Number} index The column index
4320      * @return {String} the id
4321      */
4322     getColumnId : function(index){
4323         return this.config[index].id;
4324     },
4325
4326     /**
4327      * Returns the column for a specified id.
4328      * @param {String} id The column id
4329      * @return {Object} the column
4330      */
4331     getColumnById : function(id){
4332         return this.lookup[id];
4333     },
4334
4335     
4336     /**
4337      * Returns the column for a specified dataIndex.
4338      * @param {String} dataIndex The column dataIndex
4339      * @return {Object|Boolean} the column or false if not found
4340      */
4341     getColumnByDataIndex: function(dataIndex){
4342         var index = this.findColumnIndex(dataIndex);
4343         return index > -1 ? this.config[index] : false;
4344     },
4345     
4346     /**
4347      * Returns the index for a specified column id.
4348      * @param {String} id The column id
4349      * @return {Number} the index, or -1 if not found
4350      */
4351     getIndexById : function(id){
4352         for(var i = 0, len = this.config.length; i < len; i++){
4353             if(this.config[i].id == id){
4354                 return i;
4355             }
4356         }
4357         return -1;
4358     },
4359     
4360     /**
4361      * Returns the index for a specified column dataIndex.
4362      * @param {String} dataIndex The column dataIndex
4363      * @return {Number} the index, or -1 if not found
4364      */
4365     
4366     findColumnIndex : function(dataIndex){
4367         for(var i = 0, len = this.config.length; i < len; i++){
4368             if(this.config[i].dataIndex == dataIndex){
4369                 return i;
4370             }
4371         }
4372         return -1;
4373     },
4374     
4375     
4376     moveColumn : function(oldIndex, newIndex){
4377         var c = this.config[oldIndex];
4378         this.config.splice(oldIndex, 1);
4379         this.config.splice(newIndex, 0, c);
4380         this.dataMap = null;
4381         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4382     },
4383
4384     isLocked : function(colIndex){
4385         return this.config[colIndex].locked === true;
4386     },
4387
4388     setLocked : function(colIndex, value, suppressEvent){
4389         if(this.isLocked(colIndex) == value){
4390             return;
4391         }
4392         this.config[colIndex].locked = value;
4393         if(!suppressEvent){
4394             this.fireEvent("columnlockchange", this, colIndex, value);
4395         }
4396     },
4397
4398     getTotalLockedWidth : function(){
4399         var totalWidth = 0;
4400         for(var i = 0; i < this.config.length; i++){
4401             if(this.isLocked(i) && !this.isHidden(i)){
4402                 this.totalWidth += this.getColumnWidth(i);
4403             }
4404         }
4405         return totalWidth;
4406     },
4407
4408     getLockedCount : function(){
4409         for(var i = 0, len = this.config.length; i < len; i++){
4410             if(!this.isLocked(i)){
4411                 return i;
4412             }
4413         }
4414     },
4415
4416     /**
4417      * Returns the number of columns.
4418      * @return {Number}
4419      */
4420     getColumnCount : function(visibleOnly){
4421         if(visibleOnly === true){
4422             var c = 0;
4423             for(var i = 0, len = this.config.length; i < len; i++){
4424                 if(!this.isHidden(i)){
4425                     c++;
4426                 }
4427             }
4428             return c;
4429         }
4430         return this.config.length;
4431     },
4432
4433     /**
4434      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4435      * @param {Function} fn
4436      * @param {Object} scope (optional)
4437      * @return {Array} result
4438      */
4439     getColumnsBy : function(fn, scope){
4440         var r = [];
4441         for(var i = 0, len = this.config.length; i < len; i++){
4442             var c = this.config[i];
4443             if(fn.call(scope||this, c, i) === true){
4444                 r[r.length] = c;
4445             }
4446         }
4447         return r;
4448     },
4449
4450     /**
4451      * Returns true if the specified column is sortable.
4452      * @param {Number} col The column index
4453      * @return {Boolean}
4454      */
4455     isSortable : function(col){
4456         if(typeof this.config[col].sortable == "undefined"){
4457             return this.defaultSortable;
4458         }
4459         return this.config[col].sortable;
4460     },
4461
4462     /**
4463      * Returns the rendering (formatting) function defined for the column.
4464      * @param {Number} col The column index.
4465      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4466      */
4467     getRenderer : function(col){
4468         if(!this.config[col].renderer){
4469             return Roo.grid.ColumnModel.defaultRenderer;
4470         }
4471         return this.config[col].renderer;
4472     },
4473
4474     /**
4475      * Sets the rendering (formatting) function for a column.
4476      * @param {Number} col The column index
4477      * @param {Function} fn The function to use to process the cell's raw data
4478      * to return HTML markup for the grid view. The render function is called with
4479      * the following parameters:<ul>
4480      * <li>Data value.</li>
4481      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4482      * <li>css A CSS style string to apply to the table cell.</li>
4483      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4484      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4485      * <li>Row index</li>
4486      * <li>Column index</li>
4487      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4488      */
4489     setRenderer : function(col, fn){
4490         this.config[col].renderer = fn;
4491     },
4492
4493     /**
4494      * Returns the width for the specified column.
4495      * @param {Number} col The column index
4496      * @return {Number}
4497      */
4498     getColumnWidth : function(col){
4499         return this.config[col].width * 1 || this.defaultWidth;
4500     },
4501
4502     /**
4503      * Sets the width for a column.
4504      * @param {Number} col The column index
4505      * @param {Number} width The new width
4506      */
4507     setColumnWidth : function(col, width, suppressEvent){
4508         this.config[col].width = width;
4509         this.totalWidth = null;
4510         if(!suppressEvent){
4511              this.fireEvent("widthchange", this, col, width);
4512         }
4513     },
4514
4515     /**
4516      * Returns the total width of all columns.
4517      * @param {Boolean} includeHidden True to include hidden column widths
4518      * @return {Number}
4519      */
4520     getTotalWidth : function(includeHidden){
4521         if(!this.totalWidth){
4522             this.totalWidth = 0;
4523             for(var i = 0, len = this.config.length; i < len; i++){
4524                 if(includeHidden || !this.isHidden(i)){
4525                     this.totalWidth += this.getColumnWidth(i);
4526                 }
4527             }
4528         }
4529         return this.totalWidth;
4530     },
4531
4532     /**
4533      * Returns the header for the specified column.
4534      * @param {Number} col The column index
4535      * @return {String}
4536      */
4537     getColumnHeader : function(col){
4538         return this.config[col].header;
4539     },
4540
4541     /**
4542      * Sets the header for a column.
4543      * @param {Number} col The column index
4544      * @param {String} header The new header
4545      */
4546     setColumnHeader : function(col, header){
4547         this.config[col].header = header;
4548         this.fireEvent("headerchange", this, col, header);
4549     },
4550
4551     /**
4552      * Returns the tooltip for the specified column.
4553      * @param {Number} col The column index
4554      * @return {String}
4555      */
4556     getColumnTooltip : function(col){
4557             return this.config[col].tooltip;
4558     },
4559     /**
4560      * Sets the tooltip for a column.
4561      * @param {Number} col The column index
4562      * @param {String} tooltip The new tooltip
4563      */
4564     setColumnTooltip : function(col, tooltip){
4565             this.config[col].tooltip = tooltip;
4566     },
4567
4568     /**
4569      * Returns the dataIndex for the specified column.
4570      * @param {Number} col The column index
4571      * @return {Number}
4572      */
4573     getDataIndex : function(col){
4574         return this.config[col].dataIndex;
4575     },
4576
4577     /**
4578      * Sets the dataIndex for a column.
4579      * @param {Number} col The column index
4580      * @param {Number} dataIndex The new dataIndex
4581      */
4582     setDataIndex : function(col, dataIndex){
4583         this.config[col].dataIndex = dataIndex;
4584     },
4585
4586     
4587     
4588     /**
4589      * Returns true if the cell is editable.
4590      * @param {Number} colIndex The column index
4591      * @param {Number} rowIndex The row index
4592      * @return {Boolean}
4593      */
4594     isCellEditable : function(colIndex, rowIndex){
4595         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4596     },
4597
4598     /**
4599      * Returns the editor defined for the cell/column.
4600      * return false or null to disable editing.
4601      * @param {Number} colIndex The column index
4602      * @param {Number} rowIndex The row index
4603      * @return {Object}
4604      */
4605     getCellEditor : function(colIndex, rowIndex){
4606         return this.config[colIndex].editor;
4607     },
4608
4609     /**
4610      * Sets if a column is editable.
4611      * @param {Number} col The column index
4612      * @param {Boolean} editable True if the column is editable
4613      */
4614     setEditable : function(col, editable){
4615         this.config[col].editable = editable;
4616     },
4617
4618
4619     /**
4620      * Returns true if the column is hidden.
4621      * @param {Number} colIndex The column index
4622      * @return {Boolean}
4623      */
4624     isHidden : function(colIndex){
4625         return this.config[colIndex].hidden;
4626     },
4627
4628
4629     /**
4630      * Returns true if the column width cannot be changed
4631      */
4632     isFixed : function(colIndex){
4633         return this.config[colIndex].fixed;
4634     },
4635
4636     /**
4637      * Returns true if the column can be resized
4638      * @return {Boolean}
4639      */
4640     isResizable : function(colIndex){
4641         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4642     },
4643     /**
4644      * Sets if a column is hidden.
4645      * @param {Number} colIndex The column index
4646      * @param {Boolean} hidden True if the column is hidden
4647      */
4648     setHidden : function(colIndex, hidden){
4649         this.config[colIndex].hidden = hidden;
4650         this.totalWidth = null;
4651         this.fireEvent("hiddenchange", this, colIndex, hidden);
4652     },
4653
4654     /**
4655      * Sets the editor for a column.
4656      * @param {Number} col The column index
4657      * @param {Object} editor The editor object
4658      */
4659     setEditor : function(col, editor){
4660         this.config[col].editor = editor;
4661     }
4662 });
4663
4664 Roo.grid.ColumnModel.defaultRenderer = function(value){
4665         if(typeof value == "string" && value.length < 1){
4666             return "&#160;";
4667         }
4668         return value;
4669 };
4670
4671 // Alias for backwards compatibility
4672 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4673 /*
4674  * Based on:
4675  * Ext JS Library 1.1.1
4676  * Copyright(c) 2006-2007, Ext JS, LLC.
4677  *
4678  * Originally Released Under LGPL - original licence link has changed is not relivant.
4679  *
4680  * Fork - LGPL
4681  * <script type="text/javascript">
4682  */
4683  
4684 /**
4685  * @class Roo.LoadMask
4686  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4687  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4688  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4689  * element's UpdateManager load indicator and will be destroyed after the initial load.
4690  * @constructor
4691  * Create a new LoadMask
4692  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4693  * @param {Object} config The config object
4694  */
4695 Roo.LoadMask = function(el, config){
4696     this.el = Roo.get(el);
4697     Roo.apply(this, config);
4698     if(this.store){
4699         this.store.on('beforeload', this.onBeforeLoad, this);
4700         this.store.on('load', this.onLoad, this);
4701         this.store.on('loadexception', this.onLoadException, this);
4702         this.removeMask = false;
4703     }else{
4704         var um = this.el.getUpdateManager();
4705         um.showLoadIndicator = false; // disable the default indicator
4706         um.on('beforeupdate', this.onBeforeLoad, this);
4707         um.on('update', this.onLoad, this);
4708         um.on('failure', this.onLoad, this);
4709         this.removeMask = true;
4710     }
4711 };
4712
4713 Roo.LoadMask.prototype = {
4714     /**
4715      * @cfg {Boolean} removeMask
4716      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4717      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4718      */
4719     /**
4720      * @cfg {String} msg
4721      * The text to display in a centered loading message box (defaults to 'Loading...')
4722      */
4723     msg : 'Loading...',
4724     /**
4725      * @cfg {String} msgCls
4726      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4727      */
4728     msgCls : 'x-mask-loading',
4729
4730     /**
4731      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4732      * @type Boolean
4733      */
4734     disabled: false,
4735
4736     /**
4737      * Disables the mask to prevent it from being displayed
4738      */
4739     disable : function(){
4740        this.disabled = true;
4741     },
4742
4743     /**
4744      * Enables the mask so that it can be displayed
4745      */
4746     enable : function(){
4747         this.disabled = false;
4748     },
4749     
4750     onLoadException : function()
4751     {
4752         Roo.log(arguments);
4753         
4754         if (typeof(arguments[3]) != 'undefined') {
4755             Roo.MessageBox.alert("Error loading",arguments[3]);
4756         } 
4757         /*
4758         try {
4759             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4760                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4761             }   
4762         } catch(e) {
4763             
4764         }
4765         */
4766     
4767         
4768         
4769         this.el.unmask(this.removeMask);
4770     },
4771     // private
4772     onLoad : function()
4773     {
4774         this.el.unmask(this.removeMask);
4775     },
4776
4777     // private
4778     onBeforeLoad : function(){
4779         if(!this.disabled){
4780             this.el.mask(this.msg, this.msgCls);
4781         }
4782     },
4783
4784     // private
4785     destroy : function(){
4786         if(this.store){
4787             this.store.un('beforeload', this.onBeforeLoad, this);
4788             this.store.un('load', this.onLoad, this);
4789             this.store.un('loadexception', this.onLoadException, this);
4790         }else{
4791             var um = this.el.getUpdateManager();
4792             um.un('beforeupdate', this.onBeforeLoad, this);
4793             um.un('update', this.onLoad, this);
4794             um.un('failure', this.onLoad, this);
4795         }
4796     }
4797 };/*
4798  * - LGPL
4799  *
4800  * table
4801  * 
4802  */
4803
4804 /**
4805  * @class Roo.bootstrap.Table
4806  * @extends Roo.bootstrap.Component
4807  * Bootstrap Table class
4808  * @cfg {String} cls table class
4809  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4810  * @cfg {String} bgcolor Specifies the background color for a table
4811  * @cfg {Number} border Specifies whether the table cells should have borders or not
4812  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4813  * @cfg {Number} cellspacing Specifies the space between cells
4814  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4815  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4816  * @cfg {String} sortable Specifies that the table should be sortable
4817  * @cfg {String} summary Specifies a summary of the content of a table
4818  * @cfg {Number} width Specifies the width of a table
4819  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4820  * 
4821  * @cfg {boolean} striped Should the rows be alternative striped
4822  * @cfg {boolean} bordered Add borders to the table
4823  * @cfg {boolean} hover Add hover highlighting
4824  * @cfg {boolean} condensed Format condensed
4825  * @cfg {boolean} responsive Format condensed
4826  * @cfg {Boolean} loadMask (true|false) default false
4827  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4828  * @cfg {Boolean} thead (true|false) generate thead, default true
4829  * @cfg {Boolean} RowSelection (true|false) default false
4830  * @cfg {Boolean} CellSelection (true|false) default false
4831  *
4832  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4833  
4834  * 
4835  * @constructor
4836  * Create a new Table
4837  * @param {Object} config The config object
4838  */
4839
4840 Roo.bootstrap.Table = function(config){
4841     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4842     
4843     if (this.sm) {
4844         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4845         this.sm = this.selModel;
4846         this.sm.xmodule = this.xmodule || false;
4847     }
4848     if (this.cm && typeof(this.cm.config) == 'undefined') {
4849         this.colModel = new Roo.grid.ColumnModel(this.cm);
4850         this.cm = this.colModel;
4851         this.cm.xmodule = this.xmodule || false;
4852     }
4853     if (this.store) {
4854         this.store= Roo.factory(this.store, Roo.data);
4855         this.ds = this.store;
4856         this.ds.xmodule = this.xmodule || false;
4857          
4858     }
4859     if (this.footer && this.store) {
4860         this.footer.dataSource = this.ds;
4861         this.footer = Roo.factory(this.footer);
4862     }
4863     
4864     /** @private */
4865     this.addEvents({
4866         /**
4867          * @event cellclick
4868          * Fires when a cell is clicked
4869          * @param {Roo.bootstrap.Table} this
4870          * @param {Roo.Element} el
4871          * @param {Number} rowIndex
4872          * @param {Number} columnIndex
4873          * @param {Roo.EventObject} e
4874          */
4875         "cellclick" : true,
4876         /**
4877          * @event celldblclick
4878          * Fires when a cell is double clicked
4879          * @param {Roo.bootstrap.Table} this
4880          * @param {Roo.Element} el
4881          * @param {Number} rowIndex
4882          * @param {Number} columnIndex
4883          * @param {Roo.EventObject} e
4884          */
4885         "celldblclick" : true,
4886         /**
4887          * @event rowclick
4888          * Fires when a row is clicked
4889          * @param {Roo.bootstrap.Table} this
4890          * @param {Roo.Element} el
4891          * @param {Number} rowIndex
4892          * @param {Roo.EventObject} e
4893          */
4894         "rowclick" : true,
4895         /**
4896          * @event rowdblclick
4897          * Fires when a row is double clicked
4898          * @param {Roo.bootstrap.Table} this
4899          * @param {Roo.Element} el
4900          * @param {Number} rowIndex
4901          * @param {Roo.EventObject} e
4902          */
4903         "rowdblclick" : true,
4904         /**
4905          * @event mouseover
4906          * Fires when a mouseover occur
4907          * @param {Roo.bootstrap.Table} this
4908          * @param {Roo.Element} el
4909          * @param {Number} rowIndex
4910          * @param {Number} columnIndex
4911          * @param {Roo.EventObject} e
4912          */
4913         "mouseover" : true,
4914         /**
4915          * @event mouseout
4916          * Fires when a mouseout occur
4917          * @param {Roo.bootstrap.Table} this
4918          * @param {Roo.Element} el
4919          * @param {Number} rowIndex
4920          * @param {Number} columnIndex
4921          * @param {Roo.EventObject} e
4922          */
4923         "mouseout" : true,
4924         /**
4925          * @event rowclass
4926          * Fires when a row is rendered, so you can change add a style to it.
4927          * @param {Roo.bootstrap.Table} this
4928          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4929          */
4930         'rowclass' : true
4931         
4932     });
4933 };
4934
4935 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4936     
4937     cls: false,
4938     align: false,
4939     bgcolor: false,
4940     border: false,
4941     cellpadding: false,
4942     cellspacing: false,
4943     frame: false,
4944     rules: false,
4945     sortable: false,
4946     summary: false,
4947     width: false,
4948     striped : false,
4949     bordered: false,
4950     hover:  false,
4951     condensed : false,
4952     responsive : false,
4953     sm : false,
4954     cm : false,
4955     store : false,
4956     loadMask : false,
4957     tfoot : true,
4958     thead : true,
4959     RowSelection : false,
4960     CellSelection : false,
4961     layout : false,
4962     
4963     // Roo.Element - the tbody
4964     mainBody: false, 
4965     
4966     getAutoCreate : function(){
4967         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4968         
4969         cfg = {
4970             tag: 'table',
4971             cls : 'table',
4972             cn : []
4973         }
4974             
4975         if (this.striped) {
4976             cfg.cls += ' table-striped';
4977         }
4978         
4979         if (this.hover) {
4980             cfg.cls += ' table-hover';
4981         }
4982         if (this.bordered) {
4983             cfg.cls += ' table-bordered';
4984         }
4985         if (this.condensed) {
4986             cfg.cls += ' table-condensed';
4987         }
4988         if (this.responsive) {
4989             cfg.cls += ' table-responsive';
4990         }
4991         
4992         if (this.cls) {
4993             cfg.cls+=  ' ' +this.cls;
4994         }
4995         
4996         // this lot should be simplifed...
4997         
4998         if (this.align) {
4999             cfg.align=this.align;
5000         }
5001         if (this.bgcolor) {
5002             cfg.bgcolor=this.bgcolor;
5003         }
5004         if (this.border) {
5005             cfg.border=this.border;
5006         }
5007         if (this.cellpadding) {
5008             cfg.cellpadding=this.cellpadding;
5009         }
5010         if (this.cellspacing) {
5011             cfg.cellspacing=this.cellspacing;
5012         }
5013         if (this.frame) {
5014             cfg.frame=this.frame;
5015         }
5016         if (this.rules) {
5017             cfg.rules=this.rules;
5018         }
5019         if (this.sortable) {
5020             cfg.sortable=this.sortable;
5021         }
5022         if (this.summary) {
5023             cfg.summary=this.summary;
5024         }
5025         if (this.width) {
5026             cfg.width=this.width;
5027         }
5028         if (this.layout) {
5029             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5030         }
5031         
5032         if(this.store || this.cm){
5033             if(this.thead){
5034                 cfg.cn.push(this.renderHeader());
5035             }
5036             
5037             cfg.cn.push(this.renderBody());
5038             
5039             if(this.tfoot){
5040                 cfg.cn.push(this.renderFooter());
5041             }
5042             
5043             cfg.cls+=  ' TableGrid';
5044         }
5045         
5046         return { cn : [ cfg ] };
5047     },
5048     
5049     initEvents : function()
5050     {   
5051         if(!this.store || !this.cm){
5052             return;
5053         }
5054         
5055         //Roo.log('initEvents with ds!!!!');
5056         
5057         this.mainBody = this.el.select('tbody', true).first();
5058         
5059         
5060         var _this = this;
5061         
5062         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5063             e.on('click', _this.sort, _this);
5064         });
5065         
5066         this.el.on("click", this.onClick, this);
5067         this.el.on("dblclick", this.onDblClick, this);
5068         
5069         this.parent().el.setStyle('position', 'relative');
5070         if (this.footer) {
5071             this.footer.parentId = this.id;
5072             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5073         }
5074         
5075         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5076         
5077         this.store.on('load', this.onLoad, this);
5078         this.store.on('beforeload', this.onBeforeLoad, this);
5079         this.store.on('update', this.onUpdate, this);
5080         
5081     },
5082     
5083     onMouseover : function(e, el)
5084     {
5085         var cell = Roo.get(el);
5086         
5087         if(!cell){
5088             return;
5089         }
5090         
5091         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5092             cell = cell.findParent('td', false, true);
5093         }
5094         
5095         var row = cell.findParent('tr', false, true);
5096         var cellIndex = cell.dom.cellIndex;
5097         var rowIndex = row.dom.rowIndex - 1; // start from 0
5098         
5099         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5100         
5101     },
5102     
5103     onMouseout : function(e, el)
5104     {
5105         var cell = Roo.get(el);
5106         
5107         if(!cell){
5108             return;
5109         }
5110         
5111         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5112             cell = cell.findParent('td', false, true);
5113         }
5114         
5115         var row = cell.findParent('tr', false, true);
5116         var cellIndex = cell.dom.cellIndex;
5117         var rowIndex = row.dom.rowIndex - 1; // start from 0
5118         
5119         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5120         
5121     },
5122     
5123     onClick : function(e, el)
5124     {
5125         var cell = Roo.get(el);
5126         
5127         if(!cell || (!this.CellSelection && !this.RowSelection)){
5128             return;
5129         }
5130         
5131         
5132         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5133             cell = cell.findParent('td', false, true);
5134         }
5135         
5136         var row = cell.findParent('tr', false, true);
5137         var cellIndex = cell.dom.cellIndex;
5138         var rowIndex = row.dom.rowIndex - 1;
5139         
5140         if(this.CellSelection){
5141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5142         }
5143         
5144         if(this.RowSelection){
5145             this.fireEvent('rowclick', this, row, rowIndex, e);
5146         }
5147         
5148         
5149     },
5150     
5151     onDblClick : function(e,el)
5152     {
5153         var cell = Roo.get(el);
5154         
5155         if(!cell || (!this.CellSelection && !this.RowSelection)){
5156             return;
5157         }
5158         
5159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5160             cell = cell.findParent('td', false, true);
5161         }
5162         
5163         var row = cell.findParent('tr', false, true);
5164         var cellIndex = cell.dom.cellIndex;
5165         var rowIndex = row.dom.rowIndex - 1;
5166         
5167         if(this.CellSelection){
5168             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5169         }
5170         
5171         if(this.RowSelection){
5172             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5173         }
5174     },
5175     
5176     sort : function(e,el)
5177     {
5178         var col = Roo.get(el)
5179         
5180         if(!col.hasClass('sortable')){
5181             return;
5182         }
5183         
5184         var sort = col.attr('sort');
5185         var dir = 'ASC';
5186         
5187         if(col.hasClass('glyphicon-arrow-up')){
5188             dir = 'DESC';
5189         }
5190         
5191         this.store.sortInfo = {field : sort, direction : dir};
5192         
5193         if (this.footer) {
5194             Roo.log("calling footer first");
5195             this.footer.onClick('first');
5196         } else {
5197         
5198             this.store.load({ params : { start : 0 } });
5199         }
5200     },
5201     
5202     renderHeader : function()
5203     {
5204         var header = {
5205             tag: 'thead',
5206             cn : []
5207         };
5208         
5209         var cm = this.cm;
5210         
5211         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5212             
5213             var config = cm.config[i];
5214                     
5215             var c = {
5216                 tag: 'th',
5217                 style : '',
5218                 html: cm.getColumnHeader(i)
5219             };
5220             
5221             if(typeof(config.hidden) != 'undefined' && config.hidden){
5222                 c.style += ' display:none;';
5223             }
5224             
5225             if(typeof(config.dataIndex) != 'undefined'){
5226                 c.sort = config.dataIndex;
5227             }
5228             
5229             if(typeof(config.sortable) != 'undefined' && config.sortable){
5230                 c.cls = 'sortable';
5231             }
5232             
5233             if(typeof(config.align) != 'undefined' && config.align.length){
5234                 c.style += ' text-align:' + config.align + ';';
5235             }
5236             
5237             if(typeof(config.width) != 'undefined'){
5238                 c.style += ' width:' + config.width + 'px;';
5239             }
5240             
5241             header.cn.push(c)
5242         }
5243         
5244         return header;
5245     },
5246     
5247     renderBody : function()
5248     {
5249         var body = {
5250             tag: 'tbody',
5251             cn : [
5252                 {
5253                     tag: 'tr',
5254                     cn : [
5255                         {
5256                             tag : 'td',
5257                             colspan :  this.cm.getColumnCount()
5258                         }
5259                     ]
5260                 }
5261             ]
5262         };
5263         
5264         return body;
5265     },
5266     
5267     renderFooter : function()
5268     {
5269         var footer = {
5270             tag: 'tfoot',
5271             cn : [
5272                 {
5273                     tag: 'tr',
5274                     cn : [
5275                         {
5276                             tag : 'td',
5277                             colspan :  this.cm.getColumnCount()
5278                         }
5279                     ]
5280                 }
5281             ]
5282         };
5283         
5284         return footer;
5285     },
5286     
5287     
5288     
5289     onLoad : function()
5290     {
5291         Roo.log('ds onload');
5292         this.clear();
5293         
5294         var _this = this;
5295         var cm = this.cm;
5296         var ds = this.store;
5297         
5298         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5299             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5300             
5301             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5302                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5303             }
5304             
5305             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5306                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5307             }
5308         });
5309         
5310         var tbody =  this.mainBody;
5311               
5312         if(ds.getCount() > 0){
5313             ds.data.each(function(d,rowIndex){
5314                 var row =  this.renderRow(cm, ds, rowIndex);
5315                 
5316                 tbody.createChild(row);
5317                 
5318                 var _this = this;
5319                 
5320                 if(row.cellObjects.length){
5321                     Roo.each(row.cellObjects, function(r){
5322                         _this.renderCellObject(r);
5323                     })
5324                 }
5325                 
5326             }, this);
5327         }
5328         
5329         Roo.each(this.el.select('tbody td', true).elements, function(e){
5330             e.on('mouseover', _this.onMouseover, _this);
5331         });
5332         
5333         Roo.each(this.el.select('tbody td', true).elements, function(e){
5334             e.on('mouseout', _this.onMouseout, _this);
5335         });
5336
5337         //if(this.loadMask){
5338         //    this.maskEl.hide();
5339         //}
5340     },
5341     
5342     
5343     onUpdate : function(ds,record)
5344     {
5345         this.refreshRow(record);
5346     },
5347     onRemove : function(ds, record, index, isUpdate){
5348         if(isUpdate !== true){
5349             this.fireEvent("beforerowremoved", this, index, record);
5350         }
5351         var bt = this.mainBody.dom;
5352         if(bt.rows[index]){
5353             bt.removeChild(bt.rows[index]);
5354         }
5355         
5356         if(isUpdate !== true){
5357             //this.stripeRows(index);
5358             //this.syncRowHeights(index, index);
5359             //this.layout();
5360             this.fireEvent("rowremoved", this, index, record);
5361         }
5362     },
5363     
5364     
5365     refreshRow : function(record){
5366         var ds = this.store, index;
5367         if(typeof record == 'number'){
5368             index = record;
5369             record = ds.getAt(index);
5370         }else{
5371             index = ds.indexOf(record);
5372         }
5373         this.insertRow(ds, index, true);
5374         this.onRemove(ds, record, index+1, true);
5375         //this.syncRowHeights(index, index);
5376         //this.layout();
5377         this.fireEvent("rowupdated", this, index, record);
5378     },
5379     
5380     insertRow : function(dm, rowIndex, isUpdate){
5381         
5382         if(!isUpdate){
5383             this.fireEvent("beforerowsinserted", this, rowIndex);
5384         }
5385             //var s = this.getScrollState();
5386         var row = this.renderRow(this.cm, this.store, rowIndex);
5387         // insert before rowIndex..
5388         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5389         
5390         var _this = this;
5391                 
5392         if(row.cellObjects.length){
5393             Roo.each(row.cellObjects, function(r){
5394                 _this.renderCellObject(r);
5395             })
5396         }
5397             
5398         if(!isUpdate){
5399             this.fireEvent("rowsinserted", this, rowIndex);
5400             //this.syncRowHeights(firstRow, lastRow);
5401             //this.stripeRows(firstRow);
5402             //this.layout();
5403         }
5404         
5405     },
5406     
5407     
5408     getRowDom : function(rowIndex)
5409     {
5410         // not sure if I need to check this.. but let's do it anyway..
5411         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5412                 this.mainBody.dom.rows[rowIndex] : false
5413     },
5414     // returns the object tree for a tr..
5415   
5416     
5417     renderRow : function(cm, ds, rowIndex) {
5418         
5419         var d = ds.getAt(rowIndex);
5420         
5421         var row = {
5422             tag : 'tr',
5423             cn : []
5424         };
5425             
5426         var cellObjects = [];
5427         
5428         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5429             var config = cm.config[i];
5430             
5431             var renderer = cm.getRenderer(i);
5432             var value = '';
5433             var id = false;
5434             
5435             if(typeof(renderer) !== 'undefined'){
5436                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5437             }
5438             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5439             // and are rendered into the cells after the row is rendered - using the id for the element.
5440             
5441             if(typeof(value) === 'object'){
5442                 id = Roo.id();
5443                 cellObjects.push({
5444                     container : id,
5445                     cfg : value 
5446                 })
5447             }
5448             
5449             var rowcfg = {
5450                 record: d,
5451                 rowIndex : rowIndex,
5452                 colIndex : i,
5453                 rowClass : ''
5454             }
5455
5456             this.fireEvent('rowclass', this, rowcfg);
5457             
5458             var td = {
5459                 tag: 'td',
5460                 cls : rowcfg.rowClass,
5461                 style: '',
5462                 html: (typeof(value) === 'object') ? '' : value
5463             };
5464             
5465             if (id) {
5466                 td.id = id;
5467             }
5468             
5469             if(typeof(config.hidden) != 'undefined' && config.hidden){
5470                 td.style += ' display:none;';
5471             }
5472             
5473             if(typeof(config.align) != 'undefined' && config.align.length){
5474                 td.style += ' text-align:' + config.align + ';';
5475             }
5476             
5477             if(typeof(config.width) != 'undefined'){
5478                 td.style += ' width:' +  config.width + 'px;';
5479             }
5480              
5481             row.cn.push(td);
5482            
5483         }
5484         
5485         row.cellObjects = cellObjects;
5486         
5487         return row;
5488           
5489     },
5490     
5491     
5492     
5493     onBeforeLoad : function()
5494     {
5495         //Roo.log('ds onBeforeLoad');
5496         
5497         //this.clear();
5498         
5499         //if(this.loadMask){
5500         //    this.maskEl.show();
5501         //}
5502     },
5503     
5504     clear : function()
5505     {
5506         this.el.select('tbody', true).first().dom.innerHTML = '';
5507     },
5508     
5509     getSelectionModel : function(){
5510         if(!this.selModel){
5511             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5512         }
5513         return this.selModel;
5514     },
5515     /*
5516      * Render the Roo.bootstrap object from renderder
5517      */
5518     renderCellObject : function(r)
5519     {
5520         var _this = this;
5521         
5522         var t = r.cfg.render(r.container);
5523         
5524         if(r.cfg.cn){
5525             Roo.each(r.cfg.cn, function(c){
5526                 var child = {
5527                     container: t.getChildContainer(),
5528                     cfg: c
5529                 }
5530                 _this.renderCellObject(child);
5531             })
5532         }
5533     }
5534    
5535 });
5536
5537  
5538
5539  /*
5540  * - LGPL
5541  *
5542  * table cell
5543  * 
5544  */
5545
5546 /**
5547  * @class Roo.bootstrap.TableCell
5548  * @extends Roo.bootstrap.Component
5549  * Bootstrap TableCell class
5550  * @cfg {String} html cell contain text
5551  * @cfg {String} cls cell class
5552  * @cfg {String} tag cell tag (td|th) default td
5553  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5554  * @cfg {String} align Aligns the content in a cell
5555  * @cfg {String} axis Categorizes cells
5556  * @cfg {String} bgcolor Specifies the background color of a cell
5557  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5558  * @cfg {Number} colspan Specifies the number of columns a cell should span
5559  * @cfg {String} headers Specifies one or more header cells a cell is related to
5560  * @cfg {Number} height Sets the height of a cell
5561  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5562  * @cfg {Number} rowspan Sets the number of rows a cell should span
5563  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5564  * @cfg {String} valign Vertical aligns the content in a cell
5565  * @cfg {Number} width Specifies the width of a cell
5566  * 
5567  * @constructor
5568  * Create a new TableCell
5569  * @param {Object} config The config object
5570  */
5571
5572 Roo.bootstrap.TableCell = function(config){
5573     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5574 };
5575
5576 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5577     
5578     html: false,
5579     cls: false,
5580     tag: false,
5581     abbr: false,
5582     align: false,
5583     axis: false,
5584     bgcolor: false,
5585     charoff: false,
5586     colspan: false,
5587     headers: false,
5588     height: false,
5589     nowrap: false,
5590     rowspan: false,
5591     scope: false,
5592     valign: false,
5593     width: false,
5594     
5595     
5596     getAutoCreate : function(){
5597         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5598         
5599         cfg = {
5600             tag: 'td'
5601         }
5602         
5603         if(this.tag){
5604             cfg.tag = this.tag;
5605         }
5606         
5607         if (this.html) {
5608             cfg.html=this.html
5609         }
5610         if (this.cls) {
5611             cfg.cls=this.cls
5612         }
5613         if (this.abbr) {
5614             cfg.abbr=this.abbr
5615         }
5616         if (this.align) {
5617             cfg.align=this.align
5618         }
5619         if (this.axis) {
5620             cfg.axis=this.axis
5621         }
5622         if (this.bgcolor) {
5623             cfg.bgcolor=this.bgcolor
5624         }
5625         if (this.charoff) {
5626             cfg.charoff=this.charoff
5627         }
5628         if (this.colspan) {
5629             cfg.colspan=this.colspan
5630         }
5631         if (this.headers) {
5632             cfg.headers=this.headers
5633         }
5634         if (this.height) {
5635             cfg.height=this.height
5636         }
5637         if (this.nowrap) {
5638             cfg.nowrap=this.nowrap
5639         }
5640         if (this.rowspan) {
5641             cfg.rowspan=this.rowspan
5642         }
5643         if (this.scope) {
5644             cfg.scope=this.scope
5645         }
5646         if (this.valign) {
5647             cfg.valign=this.valign
5648         }
5649         if (this.width) {
5650             cfg.width=this.width
5651         }
5652         
5653         
5654         return cfg;
5655     }
5656    
5657 });
5658
5659  
5660
5661  /*
5662  * - LGPL
5663  *
5664  * table row
5665  * 
5666  */
5667
5668 /**
5669  * @class Roo.bootstrap.TableRow
5670  * @extends Roo.bootstrap.Component
5671  * Bootstrap TableRow class
5672  * @cfg {String} cls row class
5673  * @cfg {String} align Aligns the content in a table row
5674  * @cfg {String} bgcolor Specifies a background color for a table row
5675  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5676  * @cfg {String} valign Vertical aligns the content in a table row
5677  * 
5678  * @constructor
5679  * Create a new TableRow
5680  * @param {Object} config The config object
5681  */
5682
5683 Roo.bootstrap.TableRow = function(config){
5684     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5685 };
5686
5687 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5688     
5689     cls: false,
5690     align: false,
5691     bgcolor: false,
5692     charoff: false,
5693     valign: false,
5694     
5695     getAutoCreate : function(){
5696         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5697         
5698         cfg = {
5699             tag: 'tr'
5700         }
5701             
5702         if(this.cls){
5703             cfg.cls = this.cls;
5704         }
5705         if(this.align){
5706             cfg.align = this.align;
5707         }
5708         if(this.bgcolor){
5709             cfg.bgcolor = this.bgcolor;
5710         }
5711         if(this.charoff){
5712             cfg.charoff = this.charoff;
5713         }
5714         if(this.valign){
5715             cfg.valign = this.valign;
5716         }
5717         
5718         return cfg;
5719     }
5720    
5721 });
5722
5723  
5724
5725  /*
5726  * - LGPL
5727  *
5728  * table body
5729  * 
5730  */
5731
5732 /**
5733  * @class Roo.bootstrap.TableBody
5734  * @extends Roo.bootstrap.Component
5735  * Bootstrap TableBody class
5736  * @cfg {String} cls element class
5737  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5738  * @cfg {String} align Aligns the content inside the element
5739  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5740  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5741  * 
5742  * @constructor
5743  * Create a new TableBody
5744  * @param {Object} config The config object
5745  */
5746
5747 Roo.bootstrap.TableBody = function(config){
5748     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5749 };
5750
5751 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5752     
5753     cls: false,
5754     tag: false,
5755     align: false,
5756     charoff: false,
5757     valign: false,
5758     
5759     getAutoCreate : function(){
5760         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5761         
5762         cfg = {
5763             tag: 'tbody'
5764         }
5765             
5766         if (this.cls) {
5767             cfg.cls=this.cls
5768         }
5769         if(this.tag){
5770             cfg.tag = this.tag;
5771         }
5772         
5773         if(this.align){
5774             cfg.align = this.align;
5775         }
5776         if(this.charoff){
5777             cfg.charoff = this.charoff;
5778         }
5779         if(this.valign){
5780             cfg.valign = this.valign;
5781         }
5782         
5783         return cfg;
5784     }
5785     
5786     
5787 //    initEvents : function()
5788 //    {
5789 //        
5790 //        if(!this.store){
5791 //            return;
5792 //        }
5793 //        
5794 //        this.store = Roo.factory(this.store, Roo.data);
5795 //        this.store.on('load', this.onLoad, this);
5796 //        
5797 //        this.store.load();
5798 //        
5799 //    },
5800 //    
5801 //    onLoad: function () 
5802 //    {   
5803 //        this.fireEvent('load', this);
5804 //    }
5805 //    
5806 //   
5807 });
5808
5809  
5810
5811  /*
5812  * Based on:
5813  * Ext JS Library 1.1.1
5814  * Copyright(c) 2006-2007, Ext JS, LLC.
5815  *
5816  * Originally Released Under LGPL - original licence link has changed is not relivant.
5817  *
5818  * Fork - LGPL
5819  * <script type="text/javascript">
5820  */
5821
5822 // as we use this in bootstrap.
5823 Roo.namespace('Roo.form');
5824  /**
5825  * @class Roo.form.Action
5826  * Internal Class used to handle form actions
5827  * @constructor
5828  * @param {Roo.form.BasicForm} el The form element or its id
5829  * @param {Object} config Configuration options
5830  */
5831
5832  
5833  
5834 // define the action interface
5835 Roo.form.Action = function(form, options){
5836     this.form = form;
5837     this.options = options || {};
5838 };
5839 /**
5840  * Client Validation Failed
5841  * @const 
5842  */
5843 Roo.form.Action.CLIENT_INVALID = 'client';
5844 /**
5845  * Server Validation Failed
5846  * @const 
5847  */
5848 Roo.form.Action.SERVER_INVALID = 'server';
5849  /**
5850  * Connect to Server Failed
5851  * @const 
5852  */
5853 Roo.form.Action.CONNECT_FAILURE = 'connect';
5854 /**
5855  * Reading Data from Server Failed
5856  * @const 
5857  */
5858 Roo.form.Action.LOAD_FAILURE = 'load';
5859
5860 Roo.form.Action.prototype = {
5861     type : 'default',
5862     failureType : undefined,
5863     response : undefined,
5864     result : undefined,
5865
5866     // interface method
5867     run : function(options){
5868
5869     },
5870
5871     // interface method
5872     success : function(response){
5873
5874     },
5875
5876     // interface method
5877     handleResponse : function(response){
5878
5879     },
5880
5881     // default connection failure
5882     failure : function(response){
5883         
5884         this.response = response;
5885         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5886         this.form.afterAction(this, false);
5887     },
5888
5889     processResponse : function(response){
5890         this.response = response;
5891         if(!response.responseText){
5892             return true;
5893         }
5894         this.result = this.handleResponse(response);
5895         return this.result;
5896     },
5897
5898     // utility functions used internally
5899     getUrl : function(appendParams){
5900         var url = this.options.url || this.form.url || this.form.el.dom.action;
5901         if(appendParams){
5902             var p = this.getParams();
5903             if(p){
5904                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5905             }
5906         }
5907         return url;
5908     },
5909
5910     getMethod : function(){
5911         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5912     },
5913
5914     getParams : function(){
5915         var bp = this.form.baseParams;
5916         var p = this.options.params;
5917         if(p){
5918             if(typeof p == "object"){
5919                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5920             }else if(typeof p == 'string' && bp){
5921                 p += '&' + Roo.urlEncode(bp);
5922             }
5923         }else if(bp){
5924             p = Roo.urlEncode(bp);
5925         }
5926         return p;
5927     },
5928
5929     createCallback : function(){
5930         return {
5931             success: this.success,
5932             failure: this.failure,
5933             scope: this,
5934             timeout: (this.form.timeout*1000),
5935             upload: this.form.fileUpload ? this.success : undefined
5936         };
5937     }
5938 };
5939
5940 Roo.form.Action.Submit = function(form, options){
5941     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5942 };
5943
5944 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5945     type : 'submit',
5946
5947     haveProgress : false,
5948     uploadComplete : false,
5949     
5950     // uploadProgress indicator.
5951     uploadProgress : function()
5952     {
5953         if (!this.form.progressUrl) {
5954             return;
5955         }
5956         
5957         if (!this.haveProgress) {
5958             Roo.MessageBox.progress("Uploading", "Uploading");
5959         }
5960         if (this.uploadComplete) {
5961            Roo.MessageBox.hide();
5962            return;
5963         }
5964         
5965         this.haveProgress = true;
5966    
5967         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5968         
5969         var c = new Roo.data.Connection();
5970         c.request({
5971             url : this.form.progressUrl,
5972             params: {
5973                 id : uid
5974             },
5975             method: 'GET',
5976             success : function(req){
5977                //console.log(data);
5978                 var rdata = false;
5979                 var edata;
5980                 try  {
5981                    rdata = Roo.decode(req.responseText)
5982                 } catch (e) {
5983                     Roo.log("Invalid data from server..");
5984                     Roo.log(edata);
5985                     return;
5986                 }
5987                 if (!rdata || !rdata.success) {
5988                     Roo.log(rdata);
5989                     Roo.MessageBox.alert(Roo.encode(rdata));
5990                     return;
5991                 }
5992                 var data = rdata.data;
5993                 
5994                 if (this.uploadComplete) {
5995                    Roo.MessageBox.hide();
5996                    return;
5997                 }
5998                    
5999                 if (data){
6000                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6001                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6002                     );
6003                 }
6004                 this.uploadProgress.defer(2000,this);
6005             },
6006        
6007             failure: function(data) {
6008                 Roo.log('progress url failed ');
6009                 Roo.log(data);
6010             },
6011             scope : this
6012         });
6013            
6014     },
6015     
6016     
6017     run : function()
6018     {
6019         // run get Values on the form, so it syncs any secondary forms.
6020         this.form.getValues();
6021         
6022         var o = this.options;
6023         var method = this.getMethod();
6024         var isPost = method == 'POST';
6025         if(o.clientValidation === false || this.form.isValid()){
6026             
6027             if (this.form.progressUrl) {
6028                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6029                     (new Date() * 1) + '' + Math.random());
6030                     
6031             } 
6032             
6033             
6034             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6035                 form:this.form.el.dom,
6036                 url:this.getUrl(!isPost),
6037                 method: method,
6038                 params:isPost ? this.getParams() : null,
6039                 isUpload: this.form.fileUpload
6040             }));
6041             
6042             this.uploadProgress();
6043
6044         }else if (o.clientValidation !== false){ // client validation failed
6045             this.failureType = Roo.form.Action.CLIENT_INVALID;
6046             this.form.afterAction(this, false);
6047         }
6048     },
6049
6050     success : function(response)
6051     {
6052         this.uploadComplete= true;
6053         if (this.haveProgress) {
6054             Roo.MessageBox.hide();
6055         }
6056         
6057         
6058         var result = this.processResponse(response);
6059         if(result === true || result.success){
6060             this.form.afterAction(this, true);
6061             return;
6062         }
6063         if(result.errors){
6064             this.form.markInvalid(result.errors);
6065             this.failureType = Roo.form.Action.SERVER_INVALID;
6066         }
6067         this.form.afterAction(this, false);
6068     },
6069     failure : function(response)
6070     {
6071         this.uploadComplete= true;
6072         if (this.haveProgress) {
6073             Roo.MessageBox.hide();
6074         }
6075         
6076         this.response = response;
6077         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6078         this.form.afterAction(this, false);
6079     },
6080     
6081     handleResponse : function(response){
6082         if(this.form.errorReader){
6083             var rs = this.form.errorReader.read(response);
6084             var errors = [];
6085             if(rs.records){
6086                 for(var i = 0, len = rs.records.length; i < len; i++) {
6087                     var r = rs.records[i];
6088                     errors[i] = r.data;
6089                 }
6090             }
6091             if(errors.length < 1){
6092                 errors = null;
6093             }
6094             return {
6095                 success : rs.success,
6096                 errors : errors
6097             };
6098         }
6099         var ret = false;
6100         try {
6101             ret = Roo.decode(response.responseText);
6102         } catch (e) {
6103             ret = {
6104                 success: false,
6105                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6106                 errors : []
6107             };
6108         }
6109         return ret;
6110         
6111     }
6112 });
6113
6114
6115 Roo.form.Action.Load = function(form, options){
6116     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6117     this.reader = this.form.reader;
6118 };
6119
6120 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6121     type : 'load',
6122
6123     run : function(){
6124         
6125         Roo.Ajax.request(Roo.apply(
6126                 this.createCallback(), {
6127                     method:this.getMethod(),
6128                     url:this.getUrl(false),
6129                     params:this.getParams()
6130         }));
6131     },
6132
6133     success : function(response){
6134         
6135         var result = this.processResponse(response);
6136         if(result === true || !result.success || !result.data){
6137             this.failureType = Roo.form.Action.LOAD_FAILURE;
6138             this.form.afterAction(this, false);
6139             return;
6140         }
6141         this.form.clearInvalid();
6142         this.form.setValues(result.data);
6143         this.form.afterAction(this, true);
6144     },
6145
6146     handleResponse : function(response){
6147         if(this.form.reader){
6148             var rs = this.form.reader.read(response);
6149             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6150             return {
6151                 success : rs.success,
6152                 data : data
6153             };
6154         }
6155         return Roo.decode(response.responseText);
6156     }
6157 });
6158
6159 Roo.form.Action.ACTION_TYPES = {
6160     'load' : Roo.form.Action.Load,
6161     'submit' : Roo.form.Action.Submit
6162 };/*
6163  * - LGPL
6164  *
6165  * form
6166  * 
6167  */
6168
6169 /**
6170  * @class Roo.bootstrap.Form
6171  * @extends Roo.bootstrap.Component
6172  * Bootstrap Form class
6173  * @cfg {String} method  GET | POST (default POST)
6174  * @cfg {String} labelAlign top | left (default top)
6175  * @cfg {String} align left  | right - for navbars
6176  * @cfg {Boolean} loadMask load mask when submit (default true)
6177
6178  * 
6179  * @constructor
6180  * Create a new Form
6181  * @param {Object} config The config object
6182  */
6183
6184
6185 Roo.bootstrap.Form = function(config){
6186     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6187     this.addEvents({
6188         /**
6189          * @event clientvalidation
6190          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6191          * @param {Form} this
6192          * @param {Boolean} valid true if the form has passed client-side validation
6193          */
6194         clientvalidation: true,
6195         /**
6196          * @event beforeaction
6197          * Fires before any action is performed. Return false to cancel the action.
6198          * @param {Form} this
6199          * @param {Action} action The action to be performed
6200          */
6201         beforeaction: true,
6202         /**
6203          * @event actionfailed
6204          * Fires when an action fails.
6205          * @param {Form} this
6206          * @param {Action} action The action that failed
6207          */
6208         actionfailed : true,
6209         /**
6210          * @event actioncomplete
6211          * Fires when an action is completed.
6212          * @param {Form} this
6213          * @param {Action} action The action that completed
6214          */
6215         actioncomplete : true
6216     });
6217     
6218 };
6219
6220 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6221       
6222      /**
6223      * @cfg {String} method
6224      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6225      */
6226     method : 'POST',
6227     /**
6228      * @cfg {String} url
6229      * The URL to use for form actions if one isn't supplied in the action options.
6230      */
6231     /**
6232      * @cfg {Boolean} fileUpload
6233      * Set to true if this form is a file upload.
6234      */
6235      
6236     /**
6237      * @cfg {Object} baseParams
6238      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6239      */
6240       
6241     /**
6242      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6243      */
6244     timeout: 30,
6245     /**
6246      * @cfg {Sting} align (left|right) for navbar forms
6247      */
6248     align : 'left',
6249
6250     // private
6251     activeAction : null,
6252  
6253     /**
6254      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6255      * element by passing it or its id or mask the form itself by passing in true.
6256      * @type Mixed
6257      */
6258     waitMsgTarget : false,
6259     
6260     loadMask : true,
6261     
6262     getAutoCreate : function(){
6263         
6264         var cfg = {
6265             tag: 'form',
6266             method : this.method || 'POST',
6267             id : this.id || Roo.id(),
6268             cls : ''
6269         }
6270         if (this.parent().xtype.match(/^Nav/)) {
6271             cfg.cls = 'navbar-form navbar-' + this.align;
6272             
6273         }
6274         
6275         if (this.labelAlign == 'left' ) {
6276             cfg.cls += ' form-horizontal';
6277         }
6278         
6279         
6280         return cfg;
6281     },
6282     initEvents : function()
6283     {
6284         this.el.on('submit', this.onSubmit, this);
6285         // this was added as random key presses on the form where triggering form submit.
6286         this.el.on('keypress', function(e) {
6287             if (e.getCharCode() != 13) {
6288                 return true;
6289             }
6290             // we might need to allow it for textareas.. and some other items.
6291             // check e.getTarget().
6292             
6293             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6294                 return true;
6295             }
6296         
6297             Roo.log("keypress blocked");
6298             
6299             e.preventDefault();
6300             return false;
6301         });
6302         
6303     },
6304     // private
6305     onSubmit : function(e){
6306         e.stopEvent();
6307     },
6308     
6309      /**
6310      * Returns true if client-side validation on the form is successful.
6311      * @return Boolean
6312      */
6313     isValid : function(){
6314         var items = this.getItems();
6315         var valid = true;
6316         items.each(function(f){
6317            if(!f.validate()){
6318                valid = false;
6319                
6320            }
6321         });
6322         return valid;
6323     },
6324     /**
6325      * Returns true if any fields in this form have changed since their original load.
6326      * @return Boolean
6327      */
6328     isDirty : function(){
6329         var dirty = false;
6330         var items = this.getItems();
6331         items.each(function(f){
6332            if(f.isDirty()){
6333                dirty = true;
6334                return false;
6335            }
6336            return true;
6337         });
6338         return dirty;
6339     },
6340      /**
6341      * Performs a predefined action (submit or load) or custom actions you define on this form.
6342      * @param {String} actionName The name of the action type
6343      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6344      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6345      * accept other config options):
6346      * <pre>
6347 Property          Type             Description
6348 ----------------  ---------------  ----------------------------------------------------------------------------------
6349 url               String           The url for the action (defaults to the form's url)
6350 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6351 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6352 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6353                                    validate the form on the client (defaults to false)
6354      * </pre>
6355      * @return {BasicForm} this
6356      */
6357     doAction : function(action, options){
6358         if(typeof action == 'string'){
6359             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6360         }
6361         if(this.fireEvent('beforeaction', this, action) !== false){
6362             this.beforeAction(action);
6363             action.run.defer(100, action);
6364         }
6365         return this;
6366     },
6367     
6368     // private
6369     beforeAction : function(action){
6370         var o = action.options;
6371         
6372         if(this.loadMask){
6373             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6374         }
6375         // not really supported yet.. ??
6376         
6377         //if(this.waitMsgTarget === true){
6378         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6379         //}else if(this.waitMsgTarget){
6380         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6381         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6382         //}else {
6383         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6384        // }
6385          
6386     },
6387
6388     // private
6389     afterAction : function(action, success){
6390         this.activeAction = null;
6391         var o = action.options;
6392         
6393         //if(this.waitMsgTarget === true){
6394             this.el.unmask();
6395         //}else if(this.waitMsgTarget){
6396         //    this.waitMsgTarget.unmask();
6397         //}else{
6398         //    Roo.MessageBox.updateProgress(1);
6399         //    Roo.MessageBox.hide();
6400        // }
6401         // 
6402         if(success){
6403             if(o.reset){
6404                 this.reset();
6405             }
6406             Roo.callback(o.success, o.scope, [this, action]);
6407             this.fireEvent('actioncomplete', this, action);
6408             
6409         }else{
6410             
6411             // failure condition..
6412             // we have a scenario where updates need confirming.
6413             // eg. if a locking scenario exists..
6414             // we look for { errors : { needs_confirm : true }} in the response.
6415             if (
6416                 (typeof(action.result) != 'undefined')  &&
6417                 (typeof(action.result.errors) != 'undefined')  &&
6418                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6419            ){
6420                 var _t = this;
6421                 Roo.log("not supported yet");
6422                  /*
6423                 
6424                 Roo.MessageBox.confirm(
6425                     "Change requires confirmation",
6426                     action.result.errorMsg,
6427                     function(r) {
6428                         if (r != 'yes') {
6429                             return;
6430                         }
6431                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6432                     }
6433                     
6434                 );
6435                 */
6436                 
6437                 
6438                 return;
6439             }
6440             
6441             Roo.callback(o.failure, o.scope, [this, action]);
6442             // show an error message if no failed handler is set..
6443             if (!this.hasListener('actionfailed')) {
6444                 Roo.log("need to add dialog support");
6445                 /*
6446                 Roo.MessageBox.alert("Error",
6447                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6448                         action.result.errorMsg :
6449                         "Saving Failed, please check your entries or try again"
6450                 );
6451                 */
6452             }
6453             
6454             this.fireEvent('actionfailed', this, action);
6455         }
6456         
6457     },
6458     /**
6459      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6460      * @param {String} id The value to search for
6461      * @return Field
6462      */
6463     findField : function(id){
6464         var items = this.getItems();
6465         var field = items.get(id);
6466         if(!field){
6467              items.each(function(f){
6468                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6469                     field = f;
6470                     return false;
6471                 }
6472                 return true;
6473             });
6474         }
6475         return field || null;
6476     },
6477      /**
6478      * Mark fields in this form invalid in bulk.
6479      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6480      * @return {BasicForm} this
6481      */
6482     markInvalid : function(errors){
6483         if(errors instanceof Array){
6484             for(var i = 0, len = errors.length; i < len; i++){
6485                 var fieldError = errors[i];
6486                 var f = this.findField(fieldError.id);
6487                 if(f){
6488                     f.markInvalid(fieldError.msg);
6489                 }
6490             }
6491         }else{
6492             var field, id;
6493             for(id in errors){
6494                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6495                     field.markInvalid(errors[id]);
6496                 }
6497             }
6498         }
6499         //Roo.each(this.childForms || [], function (f) {
6500         //    f.markInvalid(errors);
6501         //});
6502         
6503         return this;
6504     },
6505
6506     /**
6507      * Set values for fields in this form in bulk.
6508      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6509      * @return {BasicForm} this
6510      */
6511     setValues : function(values){
6512         if(values instanceof Array){ // array of objects
6513             for(var i = 0, len = values.length; i < len; i++){
6514                 var v = values[i];
6515                 var f = this.findField(v.id);
6516                 if(f){
6517                     f.setValue(v.value);
6518                     if(this.trackResetOnLoad){
6519                         f.originalValue = f.getValue();
6520                     }
6521                 }
6522             }
6523         }else{ // object hash
6524             var field, id;
6525             for(id in values){
6526                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6527                     
6528                     if (field.setFromData && 
6529                         field.valueField && 
6530                         field.displayField &&
6531                         // combos' with local stores can 
6532                         // be queried via setValue()
6533                         // to set their value..
6534                         (field.store && !field.store.isLocal)
6535                         ) {
6536                         // it's a combo
6537                         var sd = { };
6538                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6539                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6540                         field.setFromData(sd);
6541                         
6542                     } else {
6543                         field.setValue(values[id]);
6544                     }
6545                     
6546                     
6547                     if(this.trackResetOnLoad){
6548                         field.originalValue = field.getValue();
6549                     }
6550                 }
6551             }
6552         }
6553          
6554         //Roo.each(this.childForms || [], function (f) {
6555         //    f.setValues(values);
6556         //});
6557                 
6558         return this;
6559     },
6560
6561     /**
6562      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6563      * they are returned as an array.
6564      * @param {Boolean} asString
6565      * @return {Object}
6566      */
6567     getValues : function(asString){
6568         //if (this.childForms) {
6569             // copy values from the child forms
6570         //    Roo.each(this.childForms, function (f) {
6571         //        this.setValues(f.getValues());
6572         //    }, this);
6573         //}
6574         
6575         
6576         
6577         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6578         if(asString === true){
6579             return fs;
6580         }
6581         return Roo.urlDecode(fs);
6582     },
6583     
6584     /**
6585      * Returns the fields in this form as an object with key/value pairs. 
6586      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6587      * @return {Object}
6588      */
6589     getFieldValues : function(with_hidden)
6590     {
6591         var items = this.getItems();
6592         var ret = {};
6593         items.each(function(f){
6594             if (!f.getName()) {
6595                 return;
6596             }
6597             var v = f.getValue();
6598             if (f.inputType =='radio') {
6599                 if (typeof(ret[f.getName()]) == 'undefined') {
6600                     ret[f.getName()] = ''; // empty..
6601                 }
6602                 
6603                 if (!f.el.dom.checked) {
6604                     return;
6605                     
6606                 }
6607                 v = f.el.dom.value;
6608                 
6609             }
6610             
6611             // not sure if this supported any more..
6612             if ((typeof(v) == 'object') && f.getRawValue) {
6613                 v = f.getRawValue() ; // dates..
6614             }
6615             // combo boxes where name != hiddenName...
6616             if (f.name != f.getName()) {
6617                 ret[f.name] = f.getRawValue();
6618             }
6619             ret[f.getName()] = v;
6620         });
6621         
6622         return ret;
6623     },
6624
6625     /**
6626      * Clears all invalid messages in this form.
6627      * @return {BasicForm} this
6628      */
6629     clearInvalid : function(){
6630         var items = this.getItems();
6631         
6632         items.each(function(f){
6633            f.clearInvalid();
6634         });
6635         
6636         
6637         
6638         return this;
6639     },
6640
6641     /**
6642      * Resets this form.
6643      * @return {BasicForm} this
6644      */
6645     reset : function(){
6646         var items = this.getItems();
6647         items.each(function(f){
6648             f.reset();
6649         });
6650         
6651         Roo.each(this.childForms || [], function (f) {
6652             f.reset();
6653         });
6654        
6655         
6656         return this;
6657     },
6658     getItems : function()
6659     {
6660         var r=new Roo.util.MixedCollection(false, function(o){
6661             return o.id || (o.id = Roo.id());
6662         });
6663         var iter = function(el) {
6664             if (el.inputEl) {
6665                 r.add(el);
6666             }
6667             if (!el.items) {
6668                 return;
6669             }
6670             Roo.each(el.items,function(e) {
6671                 iter(e);
6672             });
6673             
6674             
6675         };
6676         iter(this);
6677         return r;
6678         
6679         
6680         
6681         
6682     }
6683     
6684 });
6685
6686  
6687 /*
6688  * Based on:
6689  * Ext JS Library 1.1.1
6690  * Copyright(c) 2006-2007, Ext JS, LLC.
6691  *
6692  * Originally Released Under LGPL - original licence link has changed is not relivant.
6693  *
6694  * Fork - LGPL
6695  * <script type="text/javascript">
6696  */
6697 /**
6698  * @class Roo.form.VTypes
6699  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6700  * @singleton
6701  */
6702 Roo.form.VTypes = function(){
6703     // closure these in so they are only created once.
6704     var alpha = /^[a-zA-Z_]+$/;
6705     var alphanum = /^[a-zA-Z0-9_]+$/;
6706     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6707     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6708
6709     // All these messages and functions are configurable
6710     return {
6711         /**
6712          * The function used to validate email addresses
6713          * @param {String} value The email address
6714          */
6715         'email' : function(v){
6716             return email.test(v);
6717         },
6718         /**
6719          * The error text to display when the email validation function returns false
6720          * @type String
6721          */
6722         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6723         /**
6724          * The keystroke filter mask to be applied on email input
6725          * @type RegExp
6726          */
6727         'emailMask' : /[a-z0-9_\.\-@]/i,
6728
6729         /**
6730          * The function used to validate URLs
6731          * @param {String} value The URL
6732          */
6733         'url' : function(v){
6734             return url.test(v);
6735         },
6736         /**
6737          * The error text to display when the url validation function returns false
6738          * @type String
6739          */
6740         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6741         
6742         /**
6743          * The function used to validate alpha values
6744          * @param {String} value The value
6745          */
6746         'alpha' : function(v){
6747             return alpha.test(v);
6748         },
6749         /**
6750          * The error text to display when the alpha validation function returns false
6751          * @type String
6752          */
6753         'alphaText' : 'This field should only contain letters and _',
6754         /**
6755          * The keystroke filter mask to be applied on alpha input
6756          * @type RegExp
6757          */
6758         'alphaMask' : /[a-z_]/i,
6759
6760         /**
6761          * The function used to validate alphanumeric values
6762          * @param {String} value The value
6763          */
6764         'alphanum' : function(v){
6765             return alphanum.test(v);
6766         },
6767         /**
6768          * The error text to display when the alphanumeric validation function returns false
6769          * @type String
6770          */
6771         'alphanumText' : 'This field should only contain letters, numbers and _',
6772         /**
6773          * The keystroke filter mask to be applied on alphanumeric input
6774          * @type RegExp
6775          */
6776         'alphanumMask' : /[a-z0-9_]/i
6777     };
6778 }();/*
6779  * - LGPL
6780  *
6781  * Input
6782  * 
6783  */
6784
6785 /**
6786  * @class Roo.bootstrap.Input
6787  * @extends Roo.bootstrap.Component
6788  * Bootstrap Input class
6789  * @cfg {Boolean} disabled is it disabled
6790  * @cfg {String} fieldLabel - the label associated
6791  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6792  * @cfg {String} name name of the input
6793  * @cfg {string} fieldLabel - the label associated
6794  * @cfg {string}  inputType - input / file submit ...
6795  * @cfg {string} placeholder - placeholder to put in text.
6796  * @cfg {string}  before - input group add on before
6797  * @cfg {string} after - input group add on after
6798  * @cfg {string} size - (lg|sm) or leave empty..
6799  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6800  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6801  * @cfg {Number} md colspan out of 12 for computer-sized screens
6802  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6803  * @cfg {string} value default value of the input
6804  * @cfg {Number} labelWidth set the width of label (0-12)
6805  * @cfg {String} labelAlign (top|left)
6806  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6807  * @cfg {String} align (left|center|right) Default left
6808  * 
6809  * 
6810  * @constructor
6811  * Create a new Input
6812  * @param {Object} config The config object
6813  */
6814
6815 Roo.bootstrap.Input = function(config){
6816     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6817    
6818         this.addEvents({
6819             /**
6820              * @event focus
6821              * Fires when this field receives input focus.
6822              * @param {Roo.form.Field} this
6823              */
6824             focus : true,
6825             /**
6826              * @event blur
6827              * Fires when this field loses input focus.
6828              * @param {Roo.form.Field} this
6829              */
6830             blur : true,
6831             /**
6832              * @event specialkey
6833              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6834              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6835              * @param {Roo.form.Field} this
6836              * @param {Roo.EventObject} e The event object
6837              */
6838             specialkey : true,
6839             /**
6840              * @event change
6841              * Fires just before the field blurs if the field value has changed.
6842              * @param {Roo.form.Field} this
6843              * @param {Mixed} newValue The new value
6844              * @param {Mixed} oldValue The original value
6845              */
6846             change : true,
6847             /**
6848              * @event invalid
6849              * Fires after the field has been marked as invalid.
6850              * @param {Roo.form.Field} this
6851              * @param {String} msg The validation message
6852              */
6853             invalid : true,
6854             /**
6855              * @event valid
6856              * Fires after the field has been validated with no errors.
6857              * @param {Roo.form.Field} this
6858              */
6859             valid : true,
6860              /**
6861              * @event keyup
6862              * Fires after the key up
6863              * @param {Roo.form.Field} this
6864              * @param {Roo.EventObject}  e The event Object
6865              */
6866             keyup : true
6867         });
6868 };
6869
6870 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6871      /**
6872      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6873       automatic validation (defaults to "keyup").
6874      */
6875     validationEvent : "keyup",
6876      /**
6877      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6878      */
6879     validateOnBlur : true,
6880     /**
6881      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6882      */
6883     validationDelay : 250,
6884      /**
6885      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6886      */
6887     focusClass : "x-form-focus",  // not needed???
6888     
6889        
6890     /**
6891      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6892      */
6893     invalidClass : "has-error",
6894     
6895     /**
6896      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6897      */
6898     selectOnFocus : false,
6899     
6900      /**
6901      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6902      */
6903     maskRe : null,
6904        /**
6905      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6906      */
6907     vtype : null,
6908     
6909       /**
6910      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6911      */
6912     disableKeyFilter : false,
6913     
6914        /**
6915      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6916      */
6917     disabled : false,
6918      /**
6919      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6920      */
6921     allowBlank : true,
6922     /**
6923      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6924      */
6925     blankText : "This field is required",
6926     
6927      /**
6928      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6929      */
6930     minLength : 0,
6931     /**
6932      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6933      */
6934     maxLength : Number.MAX_VALUE,
6935     /**
6936      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6937      */
6938     minLengthText : "The minimum length for this field is {0}",
6939     /**
6940      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6941      */
6942     maxLengthText : "The maximum length for this field is {0}",
6943   
6944     
6945     /**
6946      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6947      * If available, this function will be called only after the basic validators all return true, and will be passed the
6948      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6949      */
6950     validator : null,
6951     /**
6952      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6953      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6954      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6955      */
6956     regex : null,
6957     /**
6958      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6959      */
6960     regexText : "",
6961     
6962     
6963     
6964     fieldLabel : '',
6965     inputType : 'text',
6966     
6967     name : false,
6968     placeholder: false,
6969     before : false,
6970     after : false,
6971     size : false,
6972     // private
6973     hasFocus : false,
6974     preventMark: false,
6975     isFormField : true,
6976     value : '',
6977     labelWidth : 2,
6978     labelAlign : false,
6979     readOnly : false,
6980     align : false,
6981     formatedValue : false,
6982     
6983     parentLabelAlign : function()
6984     {
6985         var parent = this;
6986         while (parent.parent()) {
6987             parent = parent.parent();
6988             if (typeof(parent.labelAlign) !='undefined') {
6989                 return parent.labelAlign;
6990             }
6991         }
6992         return 'left';
6993         
6994     },
6995     
6996     getAutoCreate : function(){
6997         
6998         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6999         
7000         var id = Roo.id();
7001         
7002         var cfg = {};
7003         
7004         if(this.inputType != 'hidden'){
7005             cfg.cls = 'form-group' //input-group
7006         }
7007         
7008         var input =  {
7009             tag: 'input',
7010             id : id,
7011             type : this.inputType,
7012             value : this.value,
7013             cls : 'form-control',
7014             placeholder : this.placeholder || ''
7015             
7016         };
7017         
7018         if(this.align){
7019             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7020         }
7021         
7022         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7023             input.maxLength = this.maxLength;
7024         }
7025         
7026         if (this.disabled) {
7027             input.disabled=true;
7028         }
7029         
7030         if (this.readOnly) {
7031             input.readonly=true;
7032         }
7033         
7034         if (this.name) {
7035             input.name = this.name;
7036         }
7037         if (this.size) {
7038             input.cls += ' input-' + this.size;
7039         }
7040         var settings=this;
7041         ['xs','sm','md','lg'].map(function(size){
7042             if (settings[size]) {
7043                 cfg.cls += ' col-' + size + '-' + settings[size];
7044             }
7045         });
7046         
7047         var inputblock = input;
7048         
7049         if (this.before || this.after) {
7050             
7051             inputblock = {
7052                 cls : 'input-group',
7053                 cn :  [] 
7054             };
7055             if (this.before && typeof(this.before) == 'string') {
7056                 
7057                 inputblock.cn.push({
7058                     tag :'span',
7059                     cls : 'roo-input-before input-group-addon',
7060                     html : this.before
7061                 });
7062             }
7063             if (this.before && typeof(this.before) == 'object') {
7064                 this.before = Roo.factory(this.before);
7065                 Roo.log(this.before);
7066                 inputblock.cn.push({
7067                     tag :'span',
7068                     cls : 'roo-input-before input-group-' +
7069                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7070                 });
7071             }
7072             
7073             inputblock.cn.push(input);
7074             
7075             if (this.after && typeof(this.after) == 'string') {
7076                 inputblock.cn.push({
7077                     tag :'span',
7078                     cls : 'roo-input-after input-group-addon',
7079                     html : this.after
7080                 });
7081             }
7082             if (this.after && typeof(this.after) == 'object') {
7083                 this.after = Roo.factory(this.after);
7084                 Roo.log(this.after);
7085                 inputblock.cn.push({
7086                     tag :'span',
7087                     cls : 'roo-input-after input-group-' +
7088                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7089                 });
7090             }
7091         };
7092         
7093         if (align ==='left' && this.fieldLabel.length) {
7094                 Roo.log("left and has label");
7095                 cfg.cn = [
7096                     
7097                     {
7098                         tag: 'label',
7099                         'for' :  id,
7100                         cls : 'control-label col-sm-' + this.labelWidth,
7101                         html : this.fieldLabel
7102                         
7103                     },
7104                     {
7105                         cls : "col-sm-" + (12 - this.labelWidth), 
7106                         cn: [
7107                             inputblock
7108                         ]
7109                     }
7110                     
7111                 ];
7112         } else if ( this.fieldLabel.length) {
7113                 Roo.log(" label");
7114                  cfg.cn = [
7115                    
7116                     {
7117                         tag: 'label',
7118                         //cls : 'input-group-addon',
7119                         html : this.fieldLabel
7120                         
7121                     },
7122                     
7123                     inputblock
7124                     
7125                 ];
7126
7127         } else {
7128             
7129                 Roo.log(" no label && no align");
7130                 cfg.cn = [
7131                     
7132                         inputblock
7133                     
7134                 ];
7135                 
7136                 
7137         };
7138         Roo.log('input-parentType: ' + this.parentType);
7139         
7140         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7141            cfg.cls += ' navbar-form';
7142            Roo.log(cfg);
7143         }
7144         
7145         return cfg;
7146         
7147     },
7148     /**
7149      * return the real input element.
7150      */
7151     inputEl: function ()
7152     {
7153         return this.el.select('input.form-control',true).first();
7154     },
7155     setDisabled : function(v)
7156     {
7157         var i  = this.inputEl().dom;
7158         if (!v) {
7159             i.removeAttribute('disabled');
7160             return;
7161             
7162         }
7163         i.setAttribute('disabled','true');
7164     },
7165     initEvents : function()
7166     {
7167         
7168         this.inputEl().on("keydown" , this.fireKey,  this);
7169         this.inputEl().on("focus", this.onFocus,  this);
7170         this.inputEl().on("blur", this.onBlur,  this);
7171         
7172         this.inputEl().relayEvent('keyup', this);
7173
7174         // reference to original value for reset
7175         this.originalValue = this.getValue();
7176         //Roo.form.TextField.superclass.initEvents.call(this);
7177         if(this.validationEvent == 'keyup'){
7178             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7179             this.inputEl().on('keyup', this.filterValidation, this);
7180         }
7181         else if(this.validationEvent !== false){
7182             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7183         }
7184         
7185         if(this.selectOnFocus){
7186             this.on("focus", this.preFocus, this);
7187             
7188         }
7189         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7190             this.inputEl().on("keypress", this.filterKeys, this);
7191         }
7192        /* if(this.grow){
7193             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7194             this.el.on("click", this.autoSize,  this);
7195         }
7196         */
7197         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7198             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7199         }
7200         
7201         if (typeof(this.before) == 'object') {
7202             this.before.render(this.el.select('.roo-input-before',true).first());
7203         }
7204         if (typeof(this.after) == 'object') {
7205             this.after.render(this.el.select('.roo-input-after',true).first());
7206         }
7207         
7208         
7209     },
7210     filterValidation : function(e){
7211         if(!e.isNavKeyPress()){
7212             this.validationTask.delay(this.validationDelay);
7213         }
7214     },
7215      /**
7216      * Validates the field value
7217      * @return {Boolean} True if the value is valid, else false
7218      */
7219     validate : function(){
7220         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7221         if(this.disabled || this.validateValue(this.getRawValue())){
7222             this.clearInvalid();
7223             return true;
7224         }
7225         return false;
7226     },
7227     
7228     
7229     /**
7230      * Validates a value according to the field's validation rules and marks the field as invalid
7231      * if the validation fails
7232      * @param {Mixed} value The value to validate
7233      * @return {Boolean} True if the value is valid, else false
7234      */
7235     validateValue : function(value){
7236         if(value.length < 1)  { // if it's blank
7237              if(this.allowBlank){
7238                 this.clearInvalid();
7239                 return true;
7240              }else{
7241                 this.markInvalid(this.blankText);
7242                 return false;
7243              }
7244         }
7245         if(value.length < this.minLength){
7246             this.markInvalid(String.format(this.minLengthText, this.minLength));
7247             return false;
7248         }
7249         if(value.length > this.maxLength){
7250             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7251             return false;
7252         }
7253         if(this.vtype){
7254             var vt = Roo.form.VTypes;
7255             if(!vt[this.vtype](value, this)){
7256                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7257                 return false;
7258             }
7259         }
7260         if(typeof this.validator == "function"){
7261             var msg = this.validator(value);
7262             if(msg !== true){
7263                 this.markInvalid(msg);
7264                 return false;
7265             }
7266         }
7267         if(this.regex && !this.regex.test(value)){
7268             this.markInvalid(this.regexText);
7269             return false;
7270         }
7271         return true;
7272     },
7273
7274     
7275     
7276      // private
7277     fireKey : function(e){
7278         //Roo.log('field ' + e.getKey());
7279         if(e.isNavKeyPress()){
7280             this.fireEvent("specialkey", this, e);
7281         }
7282     },
7283     focus : function (selectText){
7284         if(this.rendered){
7285             this.inputEl().focus();
7286             if(selectText === true){
7287                 this.inputEl().dom.select();
7288             }
7289         }
7290         return this;
7291     } ,
7292     
7293     onFocus : function(){
7294         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7295            // this.el.addClass(this.focusClass);
7296         }
7297         if(!this.hasFocus){
7298             this.hasFocus = true;
7299             this.startValue = this.getValue();
7300             this.fireEvent("focus", this);
7301         }
7302     },
7303     
7304     beforeBlur : Roo.emptyFn,
7305
7306     
7307     // private
7308     onBlur : function(){
7309         this.beforeBlur();
7310         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7311             //this.el.removeClass(this.focusClass);
7312         }
7313         this.hasFocus = false;
7314         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7315             this.validate();
7316         }
7317         var v = this.getValue();
7318         if(String(v) !== String(this.startValue)){
7319             this.fireEvent('change', this, v, this.startValue);
7320         }
7321         this.fireEvent("blur", this);
7322     },
7323     
7324     /**
7325      * Resets the current field value to the originally loaded value and clears any validation messages
7326      */
7327     reset : function(){
7328         this.setValue(this.originalValue);
7329         this.clearInvalid();
7330     },
7331      /**
7332      * Returns the name of the field
7333      * @return {Mixed} name The name field
7334      */
7335     getName: function(){
7336         return this.name;
7337     },
7338      /**
7339      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7340      * @return {Mixed} value The field value
7341      */
7342     getValue : function(){
7343         
7344         var v = this.inputEl().getValue();
7345         
7346         return v;
7347     },
7348     /**
7349      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7350      * @return {Mixed} value The field value
7351      */
7352     getRawValue : function(){
7353         var v = this.inputEl().getValue();
7354         
7355         return v;
7356     },
7357     
7358     /**
7359      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7360      * @param {Mixed} value The value to set
7361      */
7362     setRawValue : function(v){
7363         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7364     },
7365     
7366     selectText : function(start, end){
7367         var v = this.getRawValue();
7368         if(v.length > 0){
7369             start = start === undefined ? 0 : start;
7370             end = end === undefined ? v.length : end;
7371             var d = this.inputEl().dom;
7372             if(d.setSelectionRange){
7373                 d.setSelectionRange(start, end);
7374             }else if(d.createTextRange){
7375                 var range = d.createTextRange();
7376                 range.moveStart("character", start);
7377                 range.moveEnd("character", v.length-end);
7378                 range.select();
7379             }
7380         }
7381     },
7382     
7383     /**
7384      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7385      * @param {Mixed} value The value to set
7386      */
7387     setValue : function(v){
7388         this.value = v;
7389         if(this.rendered){
7390             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7391             this.validate();
7392         }
7393     },
7394     
7395     /*
7396     processValue : function(value){
7397         if(this.stripCharsRe){
7398             var newValue = value.replace(this.stripCharsRe, '');
7399             if(newValue !== value){
7400                 this.setRawValue(newValue);
7401                 return newValue;
7402             }
7403         }
7404         return value;
7405     },
7406   */
7407     preFocus : function(){
7408         
7409         if(this.selectOnFocus){
7410             this.inputEl().dom.select();
7411         }
7412     },
7413     filterKeys : function(e){
7414         var k = e.getKey();
7415         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7416             return;
7417         }
7418         var c = e.getCharCode(), cc = String.fromCharCode(c);
7419         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7420             return;
7421         }
7422         if(!this.maskRe.test(cc)){
7423             e.stopEvent();
7424         }
7425     },
7426      /**
7427      * Clear any invalid styles/messages for this field
7428      */
7429     clearInvalid : function(){
7430         
7431         if(!this.el || this.preventMark){ // not rendered
7432             return;
7433         }
7434         this.el.removeClass(this.invalidClass);
7435         /*
7436         switch(this.msgTarget){
7437             case 'qtip':
7438                 this.el.dom.qtip = '';
7439                 break;
7440             case 'title':
7441                 this.el.dom.title = '';
7442                 break;
7443             case 'under':
7444                 if(this.errorEl){
7445                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7446                 }
7447                 break;
7448             case 'side':
7449                 if(this.errorIcon){
7450                     this.errorIcon.dom.qtip = '';
7451                     this.errorIcon.hide();
7452                     this.un('resize', this.alignErrorIcon, this);
7453                 }
7454                 break;
7455             default:
7456                 var t = Roo.getDom(this.msgTarget);
7457                 t.innerHTML = '';
7458                 t.style.display = 'none';
7459                 break;
7460         }
7461         */
7462         this.fireEvent('valid', this);
7463     },
7464      /**
7465      * Mark this field as invalid
7466      * @param {String} msg The validation message
7467      */
7468     markInvalid : function(msg){
7469         if(!this.el  || this.preventMark){ // not rendered
7470             return;
7471         }
7472         this.el.addClass(this.invalidClass);
7473         /*
7474         msg = msg || this.invalidText;
7475         switch(this.msgTarget){
7476             case 'qtip':
7477                 this.el.dom.qtip = msg;
7478                 this.el.dom.qclass = 'x-form-invalid-tip';
7479                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7480                     Roo.QuickTips.enable();
7481                 }
7482                 break;
7483             case 'title':
7484                 this.el.dom.title = msg;
7485                 break;
7486             case 'under':
7487                 if(!this.errorEl){
7488                     var elp = this.el.findParent('.x-form-element', 5, true);
7489                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7490                     this.errorEl.setWidth(elp.getWidth(true)-20);
7491                 }
7492                 this.errorEl.update(msg);
7493                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7494                 break;
7495             case 'side':
7496                 if(!this.errorIcon){
7497                     var elp = this.el.findParent('.x-form-element', 5, true);
7498                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7499                 }
7500                 this.alignErrorIcon();
7501                 this.errorIcon.dom.qtip = msg;
7502                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7503                 this.errorIcon.show();
7504                 this.on('resize', this.alignErrorIcon, this);
7505                 break;
7506             default:
7507                 var t = Roo.getDom(this.msgTarget);
7508                 t.innerHTML = msg;
7509                 t.style.display = this.msgDisplay;
7510                 break;
7511         }
7512         */
7513         this.fireEvent('invalid', this, msg);
7514     },
7515     // private
7516     SafariOnKeyDown : function(event)
7517     {
7518         // this is a workaround for a password hang bug on chrome/ webkit.
7519         
7520         var isSelectAll = false;
7521         
7522         if(this.inputEl().dom.selectionEnd > 0){
7523             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7524         }
7525         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7526             event.preventDefault();
7527             this.setValue('');
7528             return;
7529         }
7530         
7531         if(isSelectAll){ // backspace and delete key
7532             
7533             event.preventDefault();
7534             // this is very hacky as keydown always get's upper case.
7535             //
7536             var cc = String.fromCharCode(event.getCharCode());
7537             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7538             
7539         }
7540     },
7541     adjustWidth : function(tag, w){
7542         tag = tag.toLowerCase();
7543         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7544             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7545                 if(tag == 'input'){
7546                     return w + 2;
7547                 }
7548                 if(tag == 'textarea'){
7549                     return w-2;
7550                 }
7551             }else if(Roo.isOpera){
7552                 if(tag == 'input'){
7553                     return w + 2;
7554                 }
7555                 if(tag == 'textarea'){
7556                     return w-2;
7557                 }
7558             }
7559         }
7560         return w;
7561     }
7562     
7563 });
7564
7565  
7566 /*
7567  * - LGPL
7568  *
7569  * Input
7570  * 
7571  */
7572
7573 /**
7574  * @class Roo.bootstrap.TextArea
7575  * @extends Roo.bootstrap.Input
7576  * Bootstrap TextArea class
7577  * @cfg {Number} cols Specifies the visible width of a text area
7578  * @cfg {Number} rows Specifies the visible number of lines in a text area
7579  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7580  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7581  * @cfg {string} html text
7582  * 
7583  * @constructor
7584  * Create a new TextArea
7585  * @param {Object} config The config object
7586  */
7587
7588 Roo.bootstrap.TextArea = function(config){
7589     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7590    
7591 };
7592
7593 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7594      
7595     cols : false,
7596     rows : 5,
7597     readOnly : false,
7598     warp : 'soft',
7599     resize : false,
7600     value: false,
7601     html: false,
7602     
7603     getAutoCreate : function(){
7604         
7605         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7606         
7607         var id = Roo.id();
7608         
7609         var cfg = {};
7610         
7611         var input =  {
7612             tag: 'textarea',
7613             id : id,
7614             warp : this.warp,
7615             rows : this.rows,
7616             value : this.value || '',
7617             html: this.html || '',
7618             cls : 'form-control',
7619             placeholder : this.placeholder || '' 
7620             
7621         };
7622         
7623         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7624             input.maxLength = this.maxLength;
7625         }
7626         
7627         if(this.resize){
7628             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7629         }
7630         
7631         if(this.cols){
7632             input.cols = this.cols;
7633         }
7634         
7635         if (this.readOnly) {
7636             input.readonly = true;
7637         }
7638         
7639         if (this.name) {
7640             input.name = this.name;
7641         }
7642         
7643         if (this.size) {
7644             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7645         }
7646         
7647         var settings=this;
7648         ['xs','sm','md','lg'].map(function(size){
7649             if (settings[size]) {
7650                 cfg.cls += ' col-' + size + '-' + settings[size];
7651             }
7652         });
7653         
7654         var inputblock = input;
7655         
7656         if (this.before || this.after) {
7657             
7658             inputblock = {
7659                 cls : 'input-group',
7660                 cn :  [] 
7661             };
7662             if (this.before) {
7663                 inputblock.cn.push({
7664                     tag :'span',
7665                     cls : 'input-group-addon',
7666                     html : this.before
7667                 });
7668             }
7669             inputblock.cn.push(input);
7670             if (this.after) {
7671                 inputblock.cn.push({
7672                     tag :'span',
7673                     cls : 'input-group-addon',
7674                     html : this.after
7675                 });
7676             }
7677             
7678         }
7679         
7680         if (align ==='left' && this.fieldLabel.length) {
7681                 Roo.log("left and has label");
7682                 cfg.cn = [
7683                     
7684                     {
7685                         tag: 'label',
7686                         'for' :  id,
7687                         cls : 'control-label col-sm-' + this.labelWidth,
7688                         html : this.fieldLabel
7689                         
7690                     },
7691                     {
7692                         cls : "col-sm-" + (12 - this.labelWidth), 
7693                         cn: [
7694                             inputblock
7695                         ]
7696                     }
7697                     
7698                 ];
7699         } else if ( this.fieldLabel.length) {
7700                 Roo.log(" label");
7701                  cfg.cn = [
7702                    
7703                     {
7704                         tag: 'label',
7705                         //cls : 'input-group-addon',
7706                         html : this.fieldLabel
7707                         
7708                     },
7709                     
7710                     inputblock
7711                     
7712                 ];
7713
7714         } else {
7715             
7716                    Roo.log(" no label && no align");
7717                 cfg.cn = [
7718                     
7719                         inputblock
7720                     
7721                 ];
7722                 
7723                 
7724         }
7725         
7726         if (this.disabled) {
7727             input.disabled=true;
7728         }
7729         
7730         return cfg;
7731         
7732     },
7733     /**
7734      * return the real textarea element.
7735      */
7736     inputEl: function ()
7737     {
7738         return this.el.select('textarea.form-control',true).first();
7739     }
7740 });
7741
7742  
7743 /*
7744  * - LGPL
7745  *
7746  * trigger field - base class for combo..
7747  * 
7748  */
7749  
7750 /**
7751  * @class Roo.bootstrap.TriggerField
7752  * @extends Roo.bootstrap.Input
7753  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7754  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7755  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7756  * for which you can provide a custom implementation.  For example:
7757  * <pre><code>
7758 var trigger = new Roo.bootstrap.TriggerField();
7759 trigger.onTriggerClick = myTriggerFn;
7760 trigger.applyTo('my-field');
7761 </code></pre>
7762  *
7763  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7764  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7765  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7766  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7767  * @constructor
7768  * Create a new TriggerField.
7769  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7770  * to the base TextField)
7771  */
7772 Roo.bootstrap.TriggerField = function(config){
7773     this.mimicing = false;
7774     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7775 };
7776
7777 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7778     /**
7779      * @cfg {String} triggerClass A CSS class to apply to the trigger
7780      */
7781      /**
7782      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7783      */
7784     hideTrigger:false,
7785
7786     /** @cfg {Boolean} grow @hide */
7787     /** @cfg {Number} growMin @hide */
7788     /** @cfg {Number} growMax @hide */
7789
7790     /**
7791      * @hide 
7792      * @method
7793      */
7794     autoSize: Roo.emptyFn,
7795     // private
7796     monitorTab : true,
7797     // private
7798     deferHeight : true,
7799
7800     
7801     actionMode : 'wrap',
7802     
7803     
7804     
7805     getAutoCreate : function(){
7806        
7807         var align = this.labelAlign || this.parentLabelAlign();
7808         
7809         var id = Roo.id();
7810         
7811         var cfg = {
7812             cls: 'form-group' //input-group
7813         };
7814         
7815         
7816         var input =  {
7817             tag: 'input',
7818             id : id,
7819             type : this.inputType,
7820             cls : 'form-control',
7821             autocomplete: 'off',
7822             placeholder : this.placeholder || '' 
7823             
7824         };
7825         if (this.name) {
7826             input.name = this.name;
7827         }
7828         if (this.size) {
7829             input.cls += ' input-' + this.size;
7830         }
7831         
7832         if (this.disabled) {
7833             input.disabled=true;
7834         }
7835         
7836         var inputblock = input;
7837         
7838         if (this.before || this.after) {
7839             
7840             inputblock = {
7841                 cls : 'input-group',
7842                 cn :  [] 
7843             };
7844             if (this.before) {
7845                 inputblock.cn.push({
7846                     tag :'span',
7847                     cls : 'input-group-addon',
7848                     html : this.before
7849                 });
7850             }
7851             inputblock.cn.push(input);
7852             if (this.after) {
7853                 inputblock.cn.push({
7854                     tag :'span',
7855                     cls : 'input-group-addon',
7856                     html : this.after
7857                 });
7858             }
7859             
7860         };
7861         
7862         var box = {
7863             tag: 'div',
7864             cn: [
7865                 {
7866                     tag: 'input',
7867                     type : 'hidden',
7868                     cls: 'form-hidden-field'
7869                 },
7870                 inputblock
7871             ]
7872             
7873         };
7874         
7875         if(this.multiple){
7876             Roo.log('multiple');
7877             
7878             box = {
7879                 tag: 'div',
7880                 cn: [
7881                     {
7882                         tag: 'input',
7883                         type : 'hidden',
7884                         cls: 'form-hidden-field'
7885                     },
7886                     {
7887                         tag: 'ul',
7888                         cls: 'select2-choices',
7889                         cn:[
7890                             {
7891                                 tag: 'li',
7892                                 cls: 'select2-search-field',
7893                                 cn: [
7894
7895                                     inputblock
7896                                 ]
7897                             }
7898                         ]
7899                     }
7900                 ]
7901             }
7902         };
7903         
7904         var combobox = {
7905             cls: 'select2-container input-group',
7906             cn: [
7907                 box
7908 //                {
7909 //                    tag: 'ul',
7910 //                    cls: 'typeahead typeahead-long dropdown-menu',
7911 //                    style: 'display:none'
7912 //                }
7913             ]
7914         };
7915         
7916         if(!this.multiple && this.showToggleBtn){
7917             combobox.cn.push({
7918                 tag :'span',
7919                 cls : 'input-group-addon btn dropdown-toggle',
7920                 cn : [
7921                     {
7922                         tag: 'span',
7923                         cls: 'caret'
7924                     },
7925                     {
7926                         tag: 'span',
7927                         cls: 'combobox-clear',
7928                         cn  : [
7929                             {
7930                                 tag : 'i',
7931                                 cls: 'icon-remove'
7932                             }
7933                         ]
7934                     }
7935                 ]
7936
7937             })
7938         }
7939         
7940         if(this.multiple){
7941             combobox.cls += ' select2-container-multi';
7942         }
7943         
7944         if (align ==='left' && this.fieldLabel.length) {
7945             
7946                 Roo.log("left and has label");
7947                 cfg.cn = [
7948                     
7949                     {
7950                         tag: 'label',
7951                         'for' :  id,
7952                         cls : 'control-label col-sm-' + this.labelWidth,
7953                         html : this.fieldLabel
7954                         
7955                     },
7956                     {
7957                         cls : "col-sm-" + (12 - this.labelWidth), 
7958                         cn: [
7959                             combobox
7960                         ]
7961                     }
7962                     
7963                 ];
7964         } else if ( this.fieldLabel.length) {
7965                 Roo.log(" label");
7966                  cfg.cn = [
7967                    
7968                     {
7969                         tag: 'label',
7970                         //cls : 'input-group-addon',
7971                         html : this.fieldLabel
7972                         
7973                     },
7974                     
7975                     combobox
7976                     
7977                 ];
7978
7979         } else {
7980             
7981                 Roo.log(" no label && no align");
7982                 cfg = combobox
7983                      
7984                 
7985         }
7986          
7987         var settings=this;
7988         ['xs','sm','md','lg'].map(function(size){
7989             if (settings[size]) {
7990                 cfg.cls += ' col-' + size + '-' + settings[size];
7991             }
7992         });
7993         
7994         return cfg;
7995         
7996     },
7997     
7998     
7999     
8000     // private
8001     onResize : function(w, h){
8002 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8003 //        if(typeof w == 'number'){
8004 //            var x = w - this.trigger.getWidth();
8005 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8006 //            this.trigger.setStyle('left', x+'px');
8007 //        }
8008     },
8009
8010     // private
8011     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8012
8013     // private
8014     getResizeEl : function(){
8015         return this.inputEl();
8016     },
8017
8018     // private
8019     getPositionEl : function(){
8020         return this.inputEl();
8021     },
8022
8023     // private
8024     alignErrorIcon : function(){
8025         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8026     },
8027
8028     // private
8029     initEvents : function(){
8030         
8031         this.createList();
8032         
8033         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8034         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8035         if(!this.multiple && this.showToggleBtn){
8036             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8037             if(this.hideTrigger){
8038                 this.trigger.setDisplayed(false);
8039             }
8040             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8041         }
8042         
8043         if(this.multiple){
8044             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8045         }
8046         
8047         //this.trigger.addClassOnOver('x-form-trigger-over');
8048         //this.trigger.addClassOnClick('x-form-trigger-click');
8049         
8050         //if(!this.width){
8051         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8052         //}
8053     },
8054     
8055     createList : function()
8056     {
8057         this.list = Roo.get(document.body).createChild({
8058             tag: 'ul',
8059             cls: 'typeahead typeahead-long dropdown-menu',
8060             style: 'display:none'
8061         });
8062         
8063         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8064         
8065     },
8066
8067     // private
8068     initTrigger : function(){
8069        
8070     },
8071
8072     // private
8073     onDestroy : function(){
8074         if(this.trigger){
8075             this.trigger.removeAllListeners();
8076           //  this.trigger.remove();
8077         }
8078         //if(this.wrap){
8079         //    this.wrap.remove();
8080         //}
8081         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8082     },
8083
8084     // private
8085     onFocus : function(){
8086         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8087         /*
8088         if(!this.mimicing){
8089             this.wrap.addClass('x-trigger-wrap-focus');
8090             this.mimicing = true;
8091             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8092             if(this.monitorTab){
8093                 this.el.on("keydown", this.checkTab, this);
8094             }
8095         }
8096         */
8097     },
8098
8099     // private
8100     checkTab : function(e){
8101         if(e.getKey() == e.TAB){
8102             this.triggerBlur();
8103         }
8104     },
8105
8106     // private
8107     onBlur : function(){
8108         // do nothing
8109     },
8110
8111     // private
8112     mimicBlur : function(e, t){
8113         /*
8114         if(!this.wrap.contains(t) && this.validateBlur()){
8115             this.triggerBlur();
8116         }
8117         */
8118     },
8119
8120     // private
8121     triggerBlur : function(){
8122         this.mimicing = false;
8123         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8124         if(this.monitorTab){
8125             this.el.un("keydown", this.checkTab, this);
8126         }
8127         //this.wrap.removeClass('x-trigger-wrap-focus');
8128         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8129     },
8130
8131     // private
8132     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8133     validateBlur : function(e, t){
8134         return true;
8135     },
8136
8137     // private
8138     onDisable : function(){
8139         this.inputEl().dom.disabled = true;
8140         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8141         //if(this.wrap){
8142         //    this.wrap.addClass('x-item-disabled');
8143         //}
8144     },
8145
8146     // private
8147     onEnable : function(){
8148         this.inputEl().dom.disabled = false;
8149         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8150         //if(this.wrap){
8151         //    this.el.removeClass('x-item-disabled');
8152         //}
8153     },
8154
8155     // private
8156     onShow : function(){
8157         var ae = this.getActionEl();
8158         
8159         if(ae){
8160             ae.dom.style.display = '';
8161             ae.dom.style.visibility = 'visible';
8162         }
8163     },
8164
8165     // private
8166     
8167     onHide : function(){
8168         var ae = this.getActionEl();
8169         ae.dom.style.display = 'none';
8170     },
8171
8172     /**
8173      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8174      * by an implementing function.
8175      * @method
8176      * @param {EventObject} e
8177      */
8178     onTriggerClick : Roo.emptyFn
8179 });
8180  /*
8181  * Based on:
8182  * Ext JS Library 1.1.1
8183  * Copyright(c) 2006-2007, Ext JS, LLC.
8184  *
8185  * Originally Released Under LGPL - original licence link has changed is not relivant.
8186  *
8187  * Fork - LGPL
8188  * <script type="text/javascript">
8189  */
8190
8191
8192 /**
8193  * @class Roo.data.SortTypes
8194  * @singleton
8195  * Defines the default sorting (casting?) comparison functions used when sorting data.
8196  */
8197 Roo.data.SortTypes = {
8198     /**
8199      * Default sort that does nothing
8200      * @param {Mixed} s The value being converted
8201      * @return {Mixed} The comparison value
8202      */
8203     none : function(s){
8204         return s;
8205     },
8206     
8207     /**
8208      * The regular expression used to strip tags
8209      * @type {RegExp}
8210      * @property
8211      */
8212     stripTagsRE : /<\/?[^>]+>/gi,
8213     
8214     /**
8215      * Strips all HTML tags to sort on text only
8216      * @param {Mixed} s The value being converted
8217      * @return {String} The comparison value
8218      */
8219     asText : function(s){
8220         return String(s).replace(this.stripTagsRE, "");
8221     },
8222     
8223     /**
8224      * Strips all HTML tags to sort on text only - Case insensitive
8225      * @param {Mixed} s The value being converted
8226      * @return {String} The comparison value
8227      */
8228     asUCText : function(s){
8229         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8230     },
8231     
8232     /**
8233      * Case insensitive string
8234      * @param {Mixed} s The value being converted
8235      * @return {String} The comparison value
8236      */
8237     asUCString : function(s) {
8238         return String(s).toUpperCase();
8239     },
8240     
8241     /**
8242      * Date sorting
8243      * @param {Mixed} s The value being converted
8244      * @return {Number} The comparison value
8245      */
8246     asDate : function(s) {
8247         if(!s){
8248             return 0;
8249         }
8250         if(s instanceof Date){
8251             return s.getTime();
8252         }
8253         return Date.parse(String(s));
8254     },
8255     
8256     /**
8257      * Float sorting
8258      * @param {Mixed} s The value being converted
8259      * @return {Float} The comparison value
8260      */
8261     asFloat : function(s) {
8262         var val = parseFloat(String(s).replace(/,/g, ""));
8263         if(isNaN(val)) val = 0;
8264         return val;
8265     },
8266     
8267     /**
8268      * Integer sorting
8269      * @param {Mixed} s The value being converted
8270      * @return {Number} The comparison value
8271      */
8272     asInt : function(s) {
8273         var val = parseInt(String(s).replace(/,/g, ""));
8274         if(isNaN(val)) val = 0;
8275         return val;
8276     }
8277 };/*
8278  * Based on:
8279  * Ext JS Library 1.1.1
8280  * Copyright(c) 2006-2007, Ext JS, LLC.
8281  *
8282  * Originally Released Under LGPL - original licence link has changed is not relivant.
8283  *
8284  * Fork - LGPL
8285  * <script type="text/javascript">
8286  */
8287
8288 /**
8289 * @class Roo.data.Record
8290  * Instances of this class encapsulate both record <em>definition</em> information, and record
8291  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8292  * to access Records cached in an {@link Roo.data.Store} object.<br>
8293  * <p>
8294  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8295  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8296  * objects.<br>
8297  * <p>
8298  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8299  * @constructor
8300  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8301  * {@link #create}. The parameters are the same.
8302  * @param {Array} data An associative Array of data values keyed by the field name.
8303  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8304  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8305  * not specified an integer id is generated.
8306  */
8307 Roo.data.Record = function(data, id){
8308     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8309     this.data = data;
8310 };
8311
8312 /**
8313  * Generate a constructor for a specific record layout.
8314  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8315  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8316  * Each field definition object may contain the following properties: <ul>
8317  * <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,
8318  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8319  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8320  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8321  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8322  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8323  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8324  * this may be omitted.</p></li>
8325  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8326  * <ul><li>auto (Default, implies no conversion)</li>
8327  * <li>string</li>
8328  * <li>int</li>
8329  * <li>float</li>
8330  * <li>boolean</li>
8331  * <li>date</li></ul></p></li>
8332  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8333  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8334  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8335  * by the Reader into an object that will be stored in the Record. It is passed the
8336  * following parameters:<ul>
8337  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8338  * </ul></p></li>
8339  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8340  * </ul>
8341  * <br>usage:<br><pre><code>
8342 var TopicRecord = Roo.data.Record.create(
8343     {name: 'title', mapping: 'topic_title'},
8344     {name: 'author', mapping: 'username'},
8345     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8346     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8347     {name: 'lastPoster', mapping: 'user2'},
8348     {name: 'excerpt', mapping: 'post_text'}
8349 );
8350
8351 var myNewRecord = new TopicRecord({
8352     title: 'Do my job please',
8353     author: 'noobie',
8354     totalPosts: 1,
8355     lastPost: new Date(),
8356     lastPoster: 'Animal',
8357     excerpt: 'No way dude!'
8358 });
8359 myStore.add(myNewRecord);
8360 </code></pre>
8361  * @method create
8362  * @static
8363  */
8364 Roo.data.Record.create = function(o){
8365     var f = function(){
8366         f.superclass.constructor.apply(this, arguments);
8367     };
8368     Roo.extend(f, Roo.data.Record);
8369     var p = f.prototype;
8370     p.fields = new Roo.util.MixedCollection(false, function(field){
8371         return field.name;
8372     });
8373     for(var i = 0, len = o.length; i < len; i++){
8374         p.fields.add(new Roo.data.Field(o[i]));
8375     }
8376     f.getField = function(name){
8377         return p.fields.get(name);  
8378     };
8379     return f;
8380 };
8381
8382 Roo.data.Record.AUTO_ID = 1000;
8383 Roo.data.Record.EDIT = 'edit';
8384 Roo.data.Record.REJECT = 'reject';
8385 Roo.data.Record.COMMIT = 'commit';
8386
8387 Roo.data.Record.prototype = {
8388     /**
8389      * Readonly flag - true if this record has been modified.
8390      * @type Boolean
8391      */
8392     dirty : false,
8393     editing : false,
8394     error: null,
8395     modified: null,
8396
8397     // private
8398     join : function(store){
8399         this.store = store;
8400     },
8401
8402     /**
8403      * Set the named field to the specified value.
8404      * @param {String} name The name of the field to set.
8405      * @param {Object} value The value to set the field to.
8406      */
8407     set : function(name, value){
8408         if(this.data[name] == value){
8409             return;
8410         }
8411         this.dirty = true;
8412         if(!this.modified){
8413             this.modified = {};
8414         }
8415         if(typeof this.modified[name] == 'undefined'){
8416             this.modified[name] = this.data[name];
8417         }
8418         this.data[name] = value;
8419         if(!this.editing && this.store){
8420             this.store.afterEdit(this);
8421         }       
8422     },
8423
8424     /**
8425      * Get the value of the named field.
8426      * @param {String} name The name of the field to get the value of.
8427      * @return {Object} The value of the field.
8428      */
8429     get : function(name){
8430         return this.data[name]; 
8431     },
8432
8433     // private
8434     beginEdit : function(){
8435         this.editing = true;
8436         this.modified = {}; 
8437     },
8438
8439     // private
8440     cancelEdit : function(){
8441         this.editing = false;
8442         delete this.modified;
8443     },
8444
8445     // private
8446     endEdit : function(){
8447         this.editing = false;
8448         if(this.dirty && this.store){
8449             this.store.afterEdit(this);
8450         }
8451     },
8452
8453     /**
8454      * Usually called by the {@link Roo.data.Store} which owns the Record.
8455      * Rejects all changes made to the Record since either creation, or the last commit operation.
8456      * Modified fields are reverted to their original values.
8457      * <p>
8458      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8459      * of reject operations.
8460      */
8461     reject : function(){
8462         var m = this.modified;
8463         for(var n in m){
8464             if(typeof m[n] != "function"){
8465                 this.data[n] = m[n];
8466             }
8467         }
8468         this.dirty = false;
8469         delete this.modified;
8470         this.editing = false;
8471         if(this.store){
8472             this.store.afterReject(this);
8473         }
8474     },
8475
8476     /**
8477      * Usually called by the {@link Roo.data.Store} which owns the Record.
8478      * Commits all changes made to the Record since either creation, or the last commit operation.
8479      * <p>
8480      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8481      * of commit operations.
8482      */
8483     commit : function(){
8484         this.dirty = false;
8485         delete this.modified;
8486         this.editing = false;
8487         if(this.store){
8488             this.store.afterCommit(this);
8489         }
8490     },
8491
8492     // private
8493     hasError : function(){
8494         return this.error != null;
8495     },
8496
8497     // private
8498     clearError : function(){
8499         this.error = null;
8500     },
8501
8502     /**
8503      * Creates a copy of this record.
8504      * @param {String} id (optional) A new record id if you don't want to use this record's id
8505      * @return {Record}
8506      */
8507     copy : function(newId) {
8508         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8509     }
8510 };/*
8511  * Based on:
8512  * Ext JS Library 1.1.1
8513  * Copyright(c) 2006-2007, Ext JS, LLC.
8514  *
8515  * Originally Released Under LGPL - original licence link has changed is not relivant.
8516  *
8517  * Fork - LGPL
8518  * <script type="text/javascript">
8519  */
8520
8521
8522
8523 /**
8524  * @class Roo.data.Store
8525  * @extends Roo.util.Observable
8526  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8527  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8528  * <p>
8529  * 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
8530  * has no knowledge of the format of the data returned by the Proxy.<br>
8531  * <p>
8532  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8533  * instances from the data object. These records are cached and made available through accessor functions.
8534  * @constructor
8535  * Creates a new Store.
8536  * @param {Object} config A config object containing the objects needed for the Store to access data,
8537  * and read the data into Records.
8538  */
8539 Roo.data.Store = function(config){
8540     this.data = new Roo.util.MixedCollection(false);
8541     this.data.getKey = function(o){
8542         return o.id;
8543     };
8544     this.baseParams = {};
8545     // private
8546     this.paramNames = {
8547         "start" : "start",
8548         "limit" : "limit",
8549         "sort" : "sort",
8550         "dir" : "dir",
8551         "multisort" : "_multisort"
8552     };
8553
8554     if(config && config.data){
8555         this.inlineData = config.data;
8556         delete config.data;
8557     }
8558
8559     Roo.apply(this, config);
8560     
8561     if(this.reader){ // reader passed
8562         this.reader = Roo.factory(this.reader, Roo.data);
8563         this.reader.xmodule = this.xmodule || false;
8564         if(!this.recordType){
8565             this.recordType = this.reader.recordType;
8566         }
8567         if(this.reader.onMetaChange){
8568             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8569         }
8570     }
8571
8572     if(this.recordType){
8573         this.fields = this.recordType.prototype.fields;
8574     }
8575     this.modified = [];
8576
8577     this.addEvents({
8578         /**
8579          * @event datachanged
8580          * Fires when the data cache has changed, and a widget which is using this Store
8581          * as a Record cache should refresh its view.
8582          * @param {Store} this
8583          */
8584         datachanged : true,
8585         /**
8586          * @event metachange
8587          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8588          * @param {Store} this
8589          * @param {Object} meta The JSON metadata
8590          */
8591         metachange : true,
8592         /**
8593          * @event add
8594          * Fires when Records have been added to the Store
8595          * @param {Store} this
8596          * @param {Roo.data.Record[]} records The array of Records added
8597          * @param {Number} index The index at which the record(s) were added
8598          */
8599         add : true,
8600         /**
8601          * @event remove
8602          * Fires when a Record has been removed from the Store
8603          * @param {Store} this
8604          * @param {Roo.data.Record} record The Record that was removed
8605          * @param {Number} index The index at which the record was removed
8606          */
8607         remove : true,
8608         /**
8609          * @event update
8610          * Fires when a Record has been updated
8611          * @param {Store} this
8612          * @param {Roo.data.Record} record The Record that was updated
8613          * @param {String} operation The update operation being performed.  Value may be one of:
8614          * <pre><code>
8615  Roo.data.Record.EDIT
8616  Roo.data.Record.REJECT
8617  Roo.data.Record.COMMIT
8618          * </code></pre>
8619          */
8620         update : true,
8621         /**
8622          * @event clear
8623          * Fires when the data cache has been cleared.
8624          * @param {Store} this
8625          */
8626         clear : true,
8627         /**
8628          * @event beforeload
8629          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8630          * the load action will be canceled.
8631          * @param {Store} this
8632          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8633          */
8634         beforeload : true,
8635         /**
8636          * @event beforeloadadd
8637          * Fires after a new set of Records has been loaded.
8638          * @param {Store} this
8639          * @param {Roo.data.Record[]} records The Records that were loaded
8640          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8641          */
8642         beforeloadadd : true,
8643         /**
8644          * @event load
8645          * Fires after a new set of Records has been loaded, before they are added to the store.
8646          * @param {Store} this
8647          * @param {Roo.data.Record[]} records The Records that were loaded
8648          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8649          * @params {Object} return from reader
8650          */
8651         load : true,
8652         /**
8653          * @event loadexception
8654          * Fires if an exception occurs in the Proxy during loading.
8655          * Called with the signature of the Proxy's "loadexception" event.
8656          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8657          * 
8658          * @param {Proxy} 
8659          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8660          * @param {Object} load options 
8661          * @param {Object} jsonData from your request (normally this contains the Exception)
8662          */
8663         loadexception : true
8664     });
8665     
8666     if(this.proxy){
8667         this.proxy = Roo.factory(this.proxy, Roo.data);
8668         this.proxy.xmodule = this.xmodule || false;
8669         this.relayEvents(this.proxy,  ["loadexception"]);
8670     }
8671     this.sortToggle = {};
8672     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8673
8674     Roo.data.Store.superclass.constructor.call(this);
8675
8676     if(this.inlineData){
8677         this.loadData(this.inlineData);
8678         delete this.inlineData;
8679     }
8680 };
8681
8682 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8683      /**
8684     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8685     * without a remote query - used by combo/forms at present.
8686     */
8687     
8688     /**
8689     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8690     */
8691     /**
8692     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8693     */
8694     /**
8695     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8696     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8697     */
8698     /**
8699     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8700     * on any HTTP request
8701     */
8702     /**
8703     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8704     */
8705     /**
8706     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8707     */
8708     multiSort: false,
8709     /**
8710     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8711     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8712     */
8713     remoteSort : false,
8714
8715     /**
8716     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8717      * loaded or when a record is removed. (defaults to false).
8718     */
8719     pruneModifiedRecords : false,
8720
8721     // private
8722     lastOptions : null,
8723
8724     /**
8725      * Add Records to the Store and fires the add event.
8726      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8727      */
8728     add : function(records){
8729         records = [].concat(records);
8730         for(var i = 0, len = records.length; i < len; i++){
8731             records[i].join(this);
8732         }
8733         var index = this.data.length;
8734         this.data.addAll(records);
8735         this.fireEvent("add", this, records, index);
8736     },
8737
8738     /**
8739      * Remove a Record from the Store and fires the remove event.
8740      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8741      */
8742     remove : function(record){
8743         var index = this.data.indexOf(record);
8744         this.data.removeAt(index);
8745         if(this.pruneModifiedRecords){
8746             this.modified.remove(record);
8747         }
8748         this.fireEvent("remove", this, record, index);
8749     },
8750
8751     /**
8752      * Remove all Records from the Store and fires the clear event.
8753      */
8754     removeAll : function(){
8755         this.data.clear();
8756         if(this.pruneModifiedRecords){
8757             this.modified = [];
8758         }
8759         this.fireEvent("clear", this);
8760     },
8761
8762     /**
8763      * Inserts Records to the Store at the given index and fires the add event.
8764      * @param {Number} index The start index at which to insert the passed Records.
8765      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8766      */
8767     insert : function(index, records){
8768         records = [].concat(records);
8769         for(var i = 0, len = records.length; i < len; i++){
8770             this.data.insert(index, records[i]);
8771             records[i].join(this);
8772         }
8773         this.fireEvent("add", this, records, index);
8774     },
8775
8776     /**
8777      * Get the index within the cache of the passed Record.
8778      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8779      * @return {Number} The index of the passed Record. Returns -1 if not found.
8780      */
8781     indexOf : function(record){
8782         return this.data.indexOf(record);
8783     },
8784
8785     /**
8786      * Get the index within the cache of the Record with the passed id.
8787      * @param {String} id The id of the Record to find.
8788      * @return {Number} The index of the Record. Returns -1 if not found.
8789      */
8790     indexOfId : function(id){
8791         return this.data.indexOfKey(id);
8792     },
8793
8794     /**
8795      * Get the Record with the specified id.
8796      * @param {String} id The id of the Record to find.
8797      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8798      */
8799     getById : function(id){
8800         return this.data.key(id);
8801     },
8802
8803     /**
8804      * Get the Record at the specified index.
8805      * @param {Number} index The index of the Record to find.
8806      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8807      */
8808     getAt : function(index){
8809         return this.data.itemAt(index);
8810     },
8811
8812     /**
8813      * Returns a range of Records between specified indices.
8814      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8815      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8816      * @return {Roo.data.Record[]} An array of Records
8817      */
8818     getRange : function(start, end){
8819         return this.data.getRange(start, end);
8820     },
8821
8822     // private
8823     storeOptions : function(o){
8824         o = Roo.apply({}, o);
8825         delete o.callback;
8826         delete o.scope;
8827         this.lastOptions = o;
8828     },
8829
8830     /**
8831      * Loads the Record cache from the configured Proxy using the configured Reader.
8832      * <p>
8833      * If using remote paging, then the first load call must specify the <em>start</em>
8834      * and <em>limit</em> properties in the options.params property to establish the initial
8835      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8836      * <p>
8837      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8838      * and this call will return before the new data has been loaded. Perform any post-processing
8839      * in a callback function, or in a "load" event handler.</strong>
8840      * <p>
8841      * @param {Object} options An object containing properties which control loading options:<ul>
8842      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8843      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8844      * passed the following arguments:<ul>
8845      * <li>r : Roo.data.Record[]</li>
8846      * <li>options: Options object from the load call</li>
8847      * <li>success: Boolean success indicator</li></ul></li>
8848      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8849      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8850      * </ul>
8851      */
8852     load : function(options){
8853         options = options || {};
8854         if(this.fireEvent("beforeload", this, options) !== false){
8855             this.storeOptions(options);
8856             var p = Roo.apply(options.params || {}, this.baseParams);
8857             // if meta was not loaded from remote source.. try requesting it.
8858             if (!this.reader.metaFromRemote) {
8859                 p._requestMeta = 1;
8860             }
8861             if(this.sortInfo && this.remoteSort){
8862                 var pn = this.paramNames;
8863                 p[pn["sort"]] = this.sortInfo.field;
8864                 p[pn["dir"]] = this.sortInfo.direction;
8865             }
8866             if (this.multiSort) {
8867                 var pn = this.paramNames;
8868                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8869             }
8870             
8871             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8872         }
8873     },
8874
8875     /**
8876      * Reloads the Record cache from the configured Proxy using the configured Reader and
8877      * the options from the last load operation performed.
8878      * @param {Object} options (optional) An object containing properties which may override the options
8879      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8880      * the most recently used options are reused).
8881      */
8882     reload : function(options){
8883         this.load(Roo.applyIf(options||{}, this.lastOptions));
8884     },
8885
8886     // private
8887     // Called as a callback by the Reader during a load operation.
8888     loadRecords : function(o, options, success){
8889         if(!o || success === false){
8890             if(success !== false){
8891                 this.fireEvent("load", this, [], options, o);
8892             }
8893             if(options.callback){
8894                 options.callback.call(options.scope || this, [], options, false);
8895             }
8896             return;
8897         }
8898         // if data returned failure - throw an exception.
8899         if (o.success === false) {
8900             // show a message if no listener is registered.
8901             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8902                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8903             }
8904             // loadmask wil be hooked into this..
8905             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8906             return;
8907         }
8908         var r = o.records, t = o.totalRecords || r.length;
8909         
8910         this.fireEvent("beforeloadadd", this, r, options, o);
8911         
8912         if(!options || options.add !== true){
8913             if(this.pruneModifiedRecords){
8914                 this.modified = [];
8915             }
8916             for(var i = 0, len = r.length; i < len; i++){
8917                 r[i].join(this);
8918             }
8919             if(this.snapshot){
8920                 this.data = this.snapshot;
8921                 delete this.snapshot;
8922             }
8923             this.data.clear();
8924             this.data.addAll(r);
8925             this.totalLength = t;
8926             this.applySort();
8927             this.fireEvent("datachanged", this);
8928         }else{
8929             this.totalLength = Math.max(t, this.data.length+r.length);
8930             this.add(r);
8931         }
8932         this.fireEvent("load", this, r, options, o);
8933         if(options.callback){
8934             options.callback.call(options.scope || this, r, options, true);
8935         }
8936     },
8937
8938
8939     /**
8940      * Loads data from a passed data block. A Reader which understands the format of the data
8941      * must have been configured in the constructor.
8942      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8943      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8944      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8945      */
8946     loadData : function(o, append){
8947         var r = this.reader.readRecords(o);
8948         this.loadRecords(r, {add: append}, true);
8949     },
8950
8951     /**
8952      * Gets the number of cached records.
8953      * <p>
8954      * <em>If using paging, this may not be the total size of the dataset. If the data object
8955      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8956      * the data set size</em>
8957      */
8958     getCount : function(){
8959         return this.data.length || 0;
8960     },
8961
8962     /**
8963      * Gets the total number of records in the dataset as returned by the server.
8964      * <p>
8965      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8966      * the dataset size</em>
8967      */
8968     getTotalCount : function(){
8969         return this.totalLength || 0;
8970     },
8971
8972     /**
8973      * Returns the sort state of the Store as an object with two properties:
8974      * <pre><code>
8975  field {String} The name of the field by which the Records are sorted
8976  direction {String} The sort order, "ASC" or "DESC"
8977      * </code></pre>
8978      */
8979     getSortState : function(){
8980         return this.sortInfo;
8981     },
8982
8983     // private
8984     applySort : function(){
8985         if(this.sortInfo && !this.remoteSort){
8986             var s = this.sortInfo, f = s.field;
8987             var st = this.fields.get(f).sortType;
8988             var fn = function(r1, r2){
8989                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8990                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8991             };
8992             this.data.sort(s.direction, fn);
8993             if(this.snapshot && this.snapshot != this.data){
8994                 this.snapshot.sort(s.direction, fn);
8995             }
8996         }
8997     },
8998
8999     /**
9000      * Sets the default sort column and order to be used by the next load operation.
9001      * @param {String} fieldName The name of the field to sort by.
9002      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9003      */
9004     setDefaultSort : function(field, dir){
9005         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9006     },
9007
9008     /**
9009      * Sort the Records.
9010      * If remote sorting is used, the sort is performed on the server, and the cache is
9011      * reloaded. If local sorting is used, the cache is sorted internally.
9012      * @param {String} fieldName The name of the field to sort by.
9013      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9014      */
9015     sort : function(fieldName, dir){
9016         var f = this.fields.get(fieldName);
9017         if(!dir){
9018             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9019             
9020             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9021                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9022             }else{
9023                 dir = f.sortDir;
9024             }
9025         }
9026         this.sortToggle[f.name] = dir;
9027         this.sortInfo = {field: f.name, direction: dir};
9028         if(!this.remoteSort){
9029             this.applySort();
9030             this.fireEvent("datachanged", this);
9031         }else{
9032             this.load(this.lastOptions);
9033         }
9034     },
9035
9036     /**
9037      * Calls the specified function for each of the Records in the cache.
9038      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9039      * Returning <em>false</em> aborts and exits the iteration.
9040      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9041      */
9042     each : function(fn, scope){
9043         this.data.each(fn, scope);
9044     },
9045
9046     /**
9047      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9048      * (e.g., during paging).
9049      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9050      */
9051     getModifiedRecords : function(){
9052         return this.modified;
9053     },
9054
9055     // private
9056     createFilterFn : function(property, value, anyMatch){
9057         if(!value.exec){ // not a regex
9058             value = String(value);
9059             if(value.length == 0){
9060                 return false;
9061             }
9062             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9063         }
9064         return function(r){
9065             return value.test(r.data[property]);
9066         };
9067     },
9068
9069     /**
9070      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9071      * @param {String} property A field on your records
9072      * @param {Number} start The record index to start at (defaults to 0)
9073      * @param {Number} end The last record index to include (defaults to length - 1)
9074      * @return {Number} The sum
9075      */
9076     sum : function(property, start, end){
9077         var rs = this.data.items, v = 0;
9078         start = start || 0;
9079         end = (end || end === 0) ? end : rs.length-1;
9080
9081         for(var i = start; i <= end; i++){
9082             v += (rs[i].data[property] || 0);
9083         }
9084         return v;
9085     },
9086
9087     /**
9088      * Filter the records by a specified property.
9089      * @param {String} field A field on your records
9090      * @param {String/RegExp} value Either a string that the field
9091      * should start with or a RegExp to test against the field
9092      * @param {Boolean} anyMatch True to match any part not just the beginning
9093      */
9094     filter : function(property, value, anyMatch){
9095         var fn = this.createFilterFn(property, value, anyMatch);
9096         return fn ? this.filterBy(fn) : this.clearFilter();
9097     },
9098
9099     /**
9100      * Filter by a function. The specified function will be called with each
9101      * record in this data source. If the function returns true the record is included,
9102      * otherwise it is filtered.
9103      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9104      * @param {Object} scope (optional) The scope of the function (defaults to this)
9105      */
9106     filterBy : function(fn, scope){
9107         this.snapshot = this.snapshot || this.data;
9108         this.data = this.queryBy(fn, scope||this);
9109         this.fireEvent("datachanged", this);
9110     },
9111
9112     /**
9113      * Query the records by a specified property.
9114      * @param {String} field A field on your records
9115      * @param {String/RegExp} value Either a string that the field
9116      * should start with or a RegExp to test against the field
9117      * @param {Boolean} anyMatch True to match any part not just the beginning
9118      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9119      */
9120     query : function(property, value, anyMatch){
9121         var fn = this.createFilterFn(property, value, anyMatch);
9122         return fn ? this.queryBy(fn) : this.data.clone();
9123     },
9124
9125     /**
9126      * Query by a function. The specified function will be called with each
9127      * record in this data source. If the function returns true the record is included
9128      * in the results.
9129      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9130      * @param {Object} scope (optional) The scope of the function (defaults to this)
9131       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9132      **/
9133     queryBy : function(fn, scope){
9134         var data = this.snapshot || this.data;
9135         return data.filterBy(fn, scope||this);
9136     },
9137
9138     /**
9139      * Collects unique values for a particular dataIndex from this store.
9140      * @param {String} dataIndex The property to collect
9141      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9142      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9143      * @return {Array} An array of the unique values
9144      **/
9145     collect : function(dataIndex, allowNull, bypassFilter){
9146         var d = (bypassFilter === true && this.snapshot) ?
9147                 this.snapshot.items : this.data.items;
9148         var v, sv, r = [], l = {};
9149         for(var i = 0, len = d.length; i < len; i++){
9150             v = d[i].data[dataIndex];
9151             sv = String(v);
9152             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9153                 l[sv] = true;
9154                 r[r.length] = v;
9155             }
9156         }
9157         return r;
9158     },
9159
9160     /**
9161      * Revert to a view of the Record cache with no filtering applied.
9162      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9163      */
9164     clearFilter : function(suppressEvent){
9165         if(this.snapshot && this.snapshot != this.data){
9166             this.data = this.snapshot;
9167             delete this.snapshot;
9168             if(suppressEvent !== true){
9169                 this.fireEvent("datachanged", this);
9170             }
9171         }
9172     },
9173
9174     // private
9175     afterEdit : function(record){
9176         if(this.modified.indexOf(record) == -1){
9177             this.modified.push(record);
9178         }
9179         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9180     },
9181     
9182     // private
9183     afterReject : function(record){
9184         this.modified.remove(record);
9185         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9186     },
9187
9188     // private
9189     afterCommit : function(record){
9190         this.modified.remove(record);
9191         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9192     },
9193
9194     /**
9195      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9196      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9197      */
9198     commitChanges : function(){
9199         var m = this.modified.slice(0);
9200         this.modified = [];
9201         for(var i = 0, len = m.length; i < len; i++){
9202             m[i].commit();
9203         }
9204     },
9205
9206     /**
9207      * Cancel outstanding changes on all changed records.
9208      */
9209     rejectChanges : function(){
9210         var m = this.modified.slice(0);
9211         this.modified = [];
9212         for(var i = 0, len = m.length; i < len; i++){
9213             m[i].reject();
9214         }
9215     },
9216
9217     onMetaChange : function(meta, rtype, o){
9218         this.recordType = rtype;
9219         this.fields = rtype.prototype.fields;
9220         delete this.snapshot;
9221         this.sortInfo = meta.sortInfo || this.sortInfo;
9222         this.modified = [];
9223         this.fireEvent('metachange', this, this.reader.meta);
9224     },
9225     
9226     moveIndex : function(data, type)
9227     {
9228         var index = this.indexOf(data);
9229         
9230         var newIndex = index + type;
9231         
9232         this.remove(data);
9233         
9234         this.insert(newIndex, data);
9235         
9236     }
9237 });/*
9238  * Based on:
9239  * Ext JS Library 1.1.1
9240  * Copyright(c) 2006-2007, Ext JS, LLC.
9241  *
9242  * Originally Released Under LGPL - original licence link has changed is not relivant.
9243  *
9244  * Fork - LGPL
9245  * <script type="text/javascript">
9246  */
9247
9248 /**
9249  * @class Roo.data.SimpleStore
9250  * @extends Roo.data.Store
9251  * Small helper class to make creating Stores from Array data easier.
9252  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9253  * @cfg {Array} fields An array of field definition objects, or field name strings.
9254  * @cfg {Array} data The multi-dimensional array of data
9255  * @constructor
9256  * @param {Object} config
9257  */
9258 Roo.data.SimpleStore = function(config){
9259     Roo.data.SimpleStore.superclass.constructor.call(this, {
9260         isLocal : true,
9261         reader: new Roo.data.ArrayReader({
9262                 id: config.id
9263             },
9264             Roo.data.Record.create(config.fields)
9265         ),
9266         proxy : new Roo.data.MemoryProxy(config.data)
9267     });
9268     this.load();
9269 };
9270 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9271  * Based on:
9272  * Ext JS Library 1.1.1
9273  * Copyright(c) 2006-2007, Ext JS, LLC.
9274  *
9275  * Originally Released Under LGPL - original licence link has changed is not relivant.
9276  *
9277  * Fork - LGPL
9278  * <script type="text/javascript">
9279  */
9280
9281 /**
9282 /**
9283  * @extends Roo.data.Store
9284  * @class Roo.data.JsonStore
9285  * Small helper class to make creating Stores for JSON data easier. <br/>
9286 <pre><code>
9287 var store = new Roo.data.JsonStore({
9288     url: 'get-images.php',
9289     root: 'images',
9290     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9291 });
9292 </code></pre>
9293  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9294  * JsonReader and HttpProxy (unless inline data is provided).</b>
9295  * @cfg {Array} fields An array of field definition objects, or field name strings.
9296  * @constructor
9297  * @param {Object} config
9298  */
9299 Roo.data.JsonStore = function(c){
9300     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9301         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9302         reader: new Roo.data.JsonReader(c, c.fields)
9303     }));
9304 };
9305 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9306  * Based on:
9307  * Ext JS Library 1.1.1
9308  * Copyright(c) 2006-2007, Ext JS, LLC.
9309  *
9310  * Originally Released Under LGPL - original licence link has changed is not relivant.
9311  *
9312  * Fork - LGPL
9313  * <script type="text/javascript">
9314  */
9315
9316  
9317 Roo.data.Field = function(config){
9318     if(typeof config == "string"){
9319         config = {name: config};
9320     }
9321     Roo.apply(this, config);
9322     
9323     if(!this.type){
9324         this.type = "auto";
9325     }
9326     
9327     var st = Roo.data.SortTypes;
9328     // named sortTypes are supported, here we look them up
9329     if(typeof this.sortType == "string"){
9330         this.sortType = st[this.sortType];
9331     }
9332     
9333     // set default sortType for strings and dates
9334     if(!this.sortType){
9335         switch(this.type){
9336             case "string":
9337                 this.sortType = st.asUCString;
9338                 break;
9339             case "date":
9340                 this.sortType = st.asDate;
9341                 break;
9342             default:
9343                 this.sortType = st.none;
9344         }
9345     }
9346
9347     // define once
9348     var stripRe = /[\$,%]/g;
9349
9350     // prebuilt conversion function for this field, instead of
9351     // switching every time we're reading a value
9352     if(!this.convert){
9353         var cv, dateFormat = this.dateFormat;
9354         switch(this.type){
9355             case "":
9356             case "auto":
9357             case undefined:
9358                 cv = function(v){ return v; };
9359                 break;
9360             case "string":
9361                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9362                 break;
9363             case "int":
9364                 cv = function(v){
9365                     return v !== undefined && v !== null && v !== '' ?
9366                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9367                     };
9368                 break;
9369             case "float":
9370                 cv = function(v){
9371                     return v !== undefined && v !== null && v !== '' ?
9372                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9373                     };
9374                 break;
9375             case "bool":
9376             case "boolean":
9377                 cv = function(v){ return v === true || v === "true" || v == 1; };
9378                 break;
9379             case "date":
9380                 cv = function(v){
9381                     if(!v){
9382                         return '';
9383                     }
9384                     if(v instanceof Date){
9385                         return v;
9386                     }
9387                     if(dateFormat){
9388                         if(dateFormat == "timestamp"){
9389                             return new Date(v*1000);
9390                         }
9391                         return Date.parseDate(v, dateFormat);
9392                     }
9393                     var parsed = Date.parse(v);
9394                     return parsed ? new Date(parsed) : null;
9395                 };
9396              break;
9397             
9398         }
9399         this.convert = cv;
9400     }
9401 };
9402
9403 Roo.data.Field.prototype = {
9404     dateFormat: null,
9405     defaultValue: "",
9406     mapping: null,
9407     sortType : null,
9408     sortDir : "ASC"
9409 };/*
9410  * Based on:
9411  * Ext JS Library 1.1.1
9412  * Copyright(c) 2006-2007, Ext JS, LLC.
9413  *
9414  * Originally Released Under LGPL - original licence link has changed is not relivant.
9415  *
9416  * Fork - LGPL
9417  * <script type="text/javascript">
9418  */
9419  
9420 // Base class for reading structured data from a data source.  This class is intended to be
9421 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9422
9423 /**
9424  * @class Roo.data.DataReader
9425  * Base class for reading structured data from a data source.  This class is intended to be
9426  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9427  */
9428
9429 Roo.data.DataReader = function(meta, recordType){
9430     
9431     this.meta = meta;
9432     
9433     this.recordType = recordType instanceof Array ? 
9434         Roo.data.Record.create(recordType) : recordType;
9435 };
9436
9437 Roo.data.DataReader.prototype = {
9438      /**
9439      * Create an empty record
9440      * @param {Object} data (optional) - overlay some values
9441      * @return {Roo.data.Record} record created.
9442      */
9443     newRow :  function(d) {
9444         var da =  {};
9445         this.recordType.prototype.fields.each(function(c) {
9446             switch( c.type) {
9447                 case 'int' : da[c.name] = 0; break;
9448                 case 'date' : da[c.name] = new Date(); break;
9449                 case 'float' : da[c.name] = 0.0; break;
9450                 case 'boolean' : da[c.name] = false; break;
9451                 default : da[c.name] = ""; break;
9452             }
9453             
9454         });
9455         return new this.recordType(Roo.apply(da, d));
9456     }
9457     
9458 };/*
9459  * Based on:
9460  * Ext JS Library 1.1.1
9461  * Copyright(c) 2006-2007, Ext JS, LLC.
9462  *
9463  * Originally Released Under LGPL - original licence link has changed is not relivant.
9464  *
9465  * Fork - LGPL
9466  * <script type="text/javascript">
9467  */
9468
9469 /**
9470  * @class Roo.data.DataProxy
9471  * @extends Roo.data.Observable
9472  * This class is an abstract base class for implementations which provide retrieval of
9473  * unformatted data objects.<br>
9474  * <p>
9475  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9476  * (of the appropriate type which knows how to parse the data object) to provide a block of
9477  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9478  * <p>
9479  * Custom implementations must implement the load method as described in
9480  * {@link Roo.data.HttpProxy#load}.
9481  */
9482 Roo.data.DataProxy = function(){
9483     this.addEvents({
9484         /**
9485          * @event beforeload
9486          * Fires before a network request is made to retrieve a data object.
9487          * @param {Object} This DataProxy object.
9488          * @param {Object} params The params parameter to the load function.
9489          */
9490         beforeload : true,
9491         /**
9492          * @event load
9493          * Fires before the load method's callback is called.
9494          * @param {Object} This DataProxy object.
9495          * @param {Object} o The data object.
9496          * @param {Object} arg The callback argument object passed to the load function.
9497          */
9498         load : true,
9499         /**
9500          * @event loadexception
9501          * Fires if an Exception occurs during data retrieval.
9502          * @param {Object} This DataProxy object.
9503          * @param {Object} o The data object.
9504          * @param {Object} arg The callback argument object passed to the load function.
9505          * @param {Object} e The Exception.
9506          */
9507         loadexception : true
9508     });
9509     Roo.data.DataProxy.superclass.constructor.call(this);
9510 };
9511
9512 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9513
9514     /**
9515      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9516      */
9517 /*
9518  * Based on:
9519  * Ext JS Library 1.1.1
9520  * Copyright(c) 2006-2007, Ext JS, LLC.
9521  *
9522  * Originally Released Under LGPL - original licence link has changed is not relivant.
9523  *
9524  * Fork - LGPL
9525  * <script type="text/javascript">
9526  */
9527 /**
9528  * @class Roo.data.MemoryProxy
9529  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9530  * to the Reader when its load method is called.
9531  * @constructor
9532  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9533  */
9534 Roo.data.MemoryProxy = function(data){
9535     if (data.data) {
9536         data = data.data;
9537     }
9538     Roo.data.MemoryProxy.superclass.constructor.call(this);
9539     this.data = data;
9540 };
9541
9542 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9543     /**
9544      * Load data from the requested source (in this case an in-memory
9545      * data object passed to the constructor), read the data object into
9546      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9547      * process that block using the passed callback.
9548      * @param {Object} params This parameter is not used by the MemoryProxy class.
9549      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9550      * object into a block of Roo.data.Records.
9551      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9552      * The function must be passed <ul>
9553      * <li>The Record block object</li>
9554      * <li>The "arg" argument from the load function</li>
9555      * <li>A boolean success indicator</li>
9556      * </ul>
9557      * @param {Object} scope The scope in which to call the callback
9558      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9559      */
9560     load : function(params, reader, callback, scope, arg){
9561         params = params || {};
9562         var result;
9563         try {
9564             result = reader.readRecords(this.data);
9565         }catch(e){
9566             this.fireEvent("loadexception", this, arg, null, e);
9567             callback.call(scope, null, arg, false);
9568             return;
9569         }
9570         callback.call(scope, result, arg, true);
9571     },
9572     
9573     // private
9574     update : function(params, records){
9575         
9576     }
9577 });/*
9578  * Based on:
9579  * Ext JS Library 1.1.1
9580  * Copyright(c) 2006-2007, Ext JS, LLC.
9581  *
9582  * Originally Released Under LGPL - original licence link has changed is not relivant.
9583  *
9584  * Fork - LGPL
9585  * <script type="text/javascript">
9586  */
9587 /**
9588  * @class Roo.data.HttpProxy
9589  * @extends Roo.data.DataProxy
9590  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9591  * configured to reference a certain URL.<br><br>
9592  * <p>
9593  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9594  * from which the running page was served.<br><br>
9595  * <p>
9596  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9597  * <p>
9598  * Be aware that to enable the browser to parse an XML document, the server must set
9599  * the Content-Type header in the HTTP response to "text/xml".
9600  * @constructor
9601  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9602  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9603  * will be used to make the request.
9604  */
9605 Roo.data.HttpProxy = function(conn){
9606     Roo.data.HttpProxy.superclass.constructor.call(this);
9607     // is conn a conn config or a real conn?
9608     this.conn = conn;
9609     this.useAjax = !conn || !conn.events;
9610   
9611 };
9612
9613 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9614     // thse are take from connection...
9615     
9616     /**
9617      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9618      */
9619     /**
9620      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9621      * extra parameters to each request made by this object. (defaults to undefined)
9622      */
9623     /**
9624      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9625      *  to each request made by this object. (defaults to undefined)
9626      */
9627     /**
9628      * @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)
9629      */
9630     /**
9631      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9632      */
9633      /**
9634      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9635      * @type Boolean
9636      */
9637   
9638
9639     /**
9640      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9641      * @type Boolean
9642      */
9643     /**
9644      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9645      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9646      * a finer-grained basis than the DataProxy events.
9647      */
9648     getConnection : function(){
9649         return this.useAjax ? Roo.Ajax : this.conn;
9650     },
9651
9652     /**
9653      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9654      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9655      * process that block using the passed callback.
9656      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9657      * for the request to the remote server.
9658      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9659      * object into a block of Roo.data.Records.
9660      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9661      * The function must be passed <ul>
9662      * <li>The Record block object</li>
9663      * <li>The "arg" argument from the load function</li>
9664      * <li>A boolean success indicator</li>
9665      * </ul>
9666      * @param {Object} scope The scope in which to call the callback
9667      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9668      */
9669     load : function(params, reader, callback, scope, arg){
9670         if(this.fireEvent("beforeload", this, params) !== false){
9671             var  o = {
9672                 params : params || {},
9673                 request: {
9674                     callback : callback,
9675                     scope : scope,
9676                     arg : arg
9677                 },
9678                 reader: reader,
9679                 callback : this.loadResponse,
9680                 scope: this
9681             };
9682             if(this.useAjax){
9683                 Roo.applyIf(o, this.conn);
9684                 if(this.activeRequest){
9685                     Roo.Ajax.abort(this.activeRequest);
9686                 }
9687                 this.activeRequest = Roo.Ajax.request(o);
9688             }else{
9689                 this.conn.request(o);
9690             }
9691         }else{
9692             callback.call(scope||this, null, arg, false);
9693         }
9694     },
9695
9696     // private
9697     loadResponse : function(o, success, response){
9698         delete this.activeRequest;
9699         if(!success){
9700             this.fireEvent("loadexception", this, o, response);
9701             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9702             return;
9703         }
9704         var result;
9705         try {
9706             result = o.reader.read(response);
9707         }catch(e){
9708             this.fireEvent("loadexception", this, o, response, e);
9709             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9710             return;
9711         }
9712         
9713         this.fireEvent("load", this, o, o.request.arg);
9714         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9715     },
9716
9717     // private
9718     update : function(dataSet){
9719
9720     },
9721
9722     // private
9723     updateResponse : function(dataSet){
9724
9725     }
9726 });/*
9727  * Based on:
9728  * Ext JS Library 1.1.1
9729  * Copyright(c) 2006-2007, Ext JS, LLC.
9730  *
9731  * Originally Released Under LGPL - original licence link has changed is not relivant.
9732  *
9733  * Fork - LGPL
9734  * <script type="text/javascript">
9735  */
9736
9737 /**
9738  * @class Roo.data.ScriptTagProxy
9739  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9740  * other than the originating domain of the running page.<br><br>
9741  * <p>
9742  * <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
9743  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9744  * <p>
9745  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9746  * source code that is used as the source inside a &lt;script> tag.<br><br>
9747  * <p>
9748  * In order for the browser to process the returned data, the server must wrap the data object
9749  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9750  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9751  * depending on whether the callback name was passed:
9752  * <p>
9753  * <pre><code>
9754 boolean scriptTag = false;
9755 String cb = request.getParameter("callback");
9756 if (cb != null) {
9757     scriptTag = true;
9758     response.setContentType("text/javascript");
9759 } else {
9760     response.setContentType("application/x-json");
9761 }
9762 Writer out = response.getWriter();
9763 if (scriptTag) {
9764     out.write(cb + "(");
9765 }
9766 out.print(dataBlock.toJsonString());
9767 if (scriptTag) {
9768     out.write(");");
9769 }
9770 </pre></code>
9771  *
9772  * @constructor
9773  * @param {Object} config A configuration object.
9774  */
9775 Roo.data.ScriptTagProxy = function(config){
9776     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9777     Roo.apply(this, config);
9778     this.head = document.getElementsByTagName("head")[0];
9779 };
9780
9781 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9782
9783 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9784     /**
9785      * @cfg {String} url The URL from which to request the data object.
9786      */
9787     /**
9788      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9789      */
9790     timeout : 30000,
9791     /**
9792      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9793      * the server the name of the callback function set up by the load call to process the returned data object.
9794      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9795      * javascript output which calls this named function passing the data object as its only parameter.
9796      */
9797     callbackParam : "callback",
9798     /**
9799      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9800      * name to the request.
9801      */
9802     nocache : true,
9803
9804     /**
9805      * Load data from the configured URL, read the data object into
9806      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9807      * process that block using the passed callback.
9808      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9809      * for the request to the remote server.
9810      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9811      * object into a block of Roo.data.Records.
9812      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9813      * The function must be passed <ul>
9814      * <li>The Record block object</li>
9815      * <li>The "arg" argument from the load function</li>
9816      * <li>A boolean success indicator</li>
9817      * </ul>
9818      * @param {Object} scope The scope in which to call the callback
9819      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9820      */
9821     load : function(params, reader, callback, scope, arg){
9822         if(this.fireEvent("beforeload", this, params) !== false){
9823
9824             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9825
9826             var url = this.url;
9827             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9828             if(this.nocache){
9829                 url += "&_dc=" + (new Date().getTime());
9830             }
9831             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9832             var trans = {
9833                 id : transId,
9834                 cb : "stcCallback"+transId,
9835                 scriptId : "stcScript"+transId,
9836                 params : params,
9837                 arg : arg,
9838                 url : url,
9839                 callback : callback,
9840                 scope : scope,
9841                 reader : reader
9842             };
9843             var conn = this;
9844
9845             window[trans.cb] = function(o){
9846                 conn.handleResponse(o, trans);
9847             };
9848
9849             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9850
9851             if(this.autoAbort !== false){
9852                 this.abort();
9853             }
9854
9855             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9856
9857             var script = document.createElement("script");
9858             script.setAttribute("src", url);
9859             script.setAttribute("type", "text/javascript");
9860             script.setAttribute("id", trans.scriptId);
9861             this.head.appendChild(script);
9862
9863             this.trans = trans;
9864         }else{
9865             callback.call(scope||this, null, arg, false);
9866         }
9867     },
9868
9869     // private
9870     isLoading : function(){
9871         return this.trans ? true : false;
9872     },
9873
9874     /**
9875      * Abort the current server request.
9876      */
9877     abort : function(){
9878         if(this.isLoading()){
9879             this.destroyTrans(this.trans);
9880         }
9881     },
9882
9883     // private
9884     destroyTrans : function(trans, isLoaded){
9885         this.head.removeChild(document.getElementById(trans.scriptId));
9886         clearTimeout(trans.timeoutId);
9887         if(isLoaded){
9888             window[trans.cb] = undefined;
9889             try{
9890                 delete window[trans.cb];
9891             }catch(e){}
9892         }else{
9893             // if hasn't been loaded, wait for load to remove it to prevent script error
9894             window[trans.cb] = function(){
9895                 window[trans.cb] = undefined;
9896                 try{
9897                     delete window[trans.cb];
9898                 }catch(e){}
9899             };
9900         }
9901     },
9902
9903     // private
9904     handleResponse : function(o, trans){
9905         this.trans = false;
9906         this.destroyTrans(trans, true);
9907         var result;
9908         try {
9909             result = trans.reader.readRecords(o);
9910         }catch(e){
9911             this.fireEvent("loadexception", this, o, trans.arg, e);
9912             trans.callback.call(trans.scope||window, null, trans.arg, false);
9913             return;
9914         }
9915         this.fireEvent("load", this, o, trans.arg);
9916         trans.callback.call(trans.scope||window, result, trans.arg, true);
9917     },
9918
9919     // private
9920     handleFailure : function(trans){
9921         this.trans = false;
9922         this.destroyTrans(trans, false);
9923         this.fireEvent("loadexception", this, null, trans.arg);
9924         trans.callback.call(trans.scope||window, null, trans.arg, false);
9925     }
9926 });/*
9927  * Based on:
9928  * Ext JS Library 1.1.1
9929  * Copyright(c) 2006-2007, Ext JS, LLC.
9930  *
9931  * Originally Released Under LGPL - original licence link has changed is not relivant.
9932  *
9933  * Fork - LGPL
9934  * <script type="text/javascript">
9935  */
9936
9937 /**
9938  * @class Roo.data.JsonReader
9939  * @extends Roo.data.DataReader
9940  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9941  * based on mappings in a provided Roo.data.Record constructor.
9942  * 
9943  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9944  * in the reply previously. 
9945  * 
9946  * <p>
9947  * Example code:
9948  * <pre><code>
9949 var RecordDef = Roo.data.Record.create([
9950     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9951     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9952 ]);
9953 var myReader = new Roo.data.JsonReader({
9954     totalProperty: "results",    // The property which contains the total dataset size (optional)
9955     root: "rows",                // The property which contains an Array of row objects
9956     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9957 }, RecordDef);
9958 </code></pre>
9959  * <p>
9960  * This would consume a JSON file like this:
9961  * <pre><code>
9962 { 'results': 2, 'rows': [
9963     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9964     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9965 }
9966 </code></pre>
9967  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9968  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9969  * paged from the remote server.
9970  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9971  * @cfg {String} root name of the property which contains the Array of row objects.
9972  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9973  * @constructor
9974  * Create a new JsonReader
9975  * @param {Object} meta Metadata configuration options
9976  * @param {Object} recordType Either an Array of field definition objects,
9977  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9978  */
9979 Roo.data.JsonReader = function(meta, recordType){
9980     
9981     meta = meta || {};
9982     // set some defaults:
9983     Roo.applyIf(meta, {
9984         totalProperty: 'total',
9985         successProperty : 'success',
9986         root : 'data',
9987         id : 'id'
9988     });
9989     
9990     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9991 };
9992 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9993     
9994     /**
9995      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9996      * Used by Store query builder to append _requestMeta to params.
9997      * 
9998      */
9999     metaFromRemote : false,
10000     /**
10001      * This method is only used by a DataProxy which has retrieved data from a remote server.
10002      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10003      * @return {Object} data A data block which is used by an Roo.data.Store object as
10004      * a cache of Roo.data.Records.
10005      */
10006     read : function(response){
10007         var json = response.responseText;
10008        
10009         var o = /* eval:var:o */ eval("("+json+")");
10010         if(!o) {
10011             throw {message: "JsonReader.read: Json object not found"};
10012         }
10013         
10014         if(o.metaData){
10015             
10016             delete this.ef;
10017             this.metaFromRemote = true;
10018             this.meta = o.metaData;
10019             this.recordType = Roo.data.Record.create(o.metaData.fields);
10020             this.onMetaChange(this.meta, this.recordType, o);
10021         }
10022         return this.readRecords(o);
10023     },
10024
10025     // private function a store will implement
10026     onMetaChange : function(meta, recordType, o){
10027
10028     },
10029
10030     /**
10031          * @ignore
10032          */
10033     simpleAccess: function(obj, subsc) {
10034         return obj[subsc];
10035     },
10036
10037         /**
10038          * @ignore
10039          */
10040     getJsonAccessor: function(){
10041         var re = /[\[\.]/;
10042         return function(expr) {
10043             try {
10044                 return(re.test(expr))
10045                     ? new Function("obj", "return obj." + expr)
10046                     : function(obj){
10047                         return obj[expr];
10048                     };
10049             } catch(e){}
10050             return Roo.emptyFn;
10051         };
10052     }(),
10053
10054     /**
10055      * Create a data block containing Roo.data.Records from an XML document.
10056      * @param {Object} o An object which contains an Array of row objects in the property specified
10057      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10058      * which contains the total size of the dataset.
10059      * @return {Object} data A data block which is used by an Roo.data.Store object as
10060      * a cache of Roo.data.Records.
10061      */
10062     readRecords : function(o){
10063         /**
10064          * After any data loads, the raw JSON data is available for further custom processing.
10065          * @type Object
10066          */
10067         this.o = o;
10068         var s = this.meta, Record = this.recordType,
10069             f = Record.prototype.fields, fi = f.items, fl = f.length;
10070
10071 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10072         if (!this.ef) {
10073             if(s.totalProperty) {
10074                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10075                 }
10076                 if(s.successProperty) {
10077                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10078                 }
10079                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10080                 if (s.id) {
10081                         var g = this.getJsonAccessor(s.id);
10082                         this.getId = function(rec) {
10083                                 var r = g(rec);
10084                                 return (r === undefined || r === "") ? null : r;
10085                         };
10086                 } else {
10087                         this.getId = function(){return null;};
10088                 }
10089             this.ef = [];
10090             for(var jj = 0; jj < fl; jj++){
10091                 f = fi[jj];
10092                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10093                 this.ef[jj] = this.getJsonAccessor(map);
10094             }
10095         }
10096
10097         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10098         if(s.totalProperty){
10099             var vt = parseInt(this.getTotal(o), 10);
10100             if(!isNaN(vt)){
10101                 totalRecords = vt;
10102             }
10103         }
10104         if(s.successProperty){
10105             var vs = this.getSuccess(o);
10106             if(vs === false || vs === 'false'){
10107                 success = false;
10108             }
10109         }
10110         var records = [];
10111             for(var i = 0; i < c; i++){
10112                     var n = root[i];
10113                 var values = {};
10114                 var id = this.getId(n);
10115                 for(var j = 0; j < fl; j++){
10116                     f = fi[j];
10117                 var v = this.ef[j](n);
10118                 if (!f.convert) {
10119                     Roo.log('missing convert for ' + f.name);
10120                     Roo.log(f);
10121                     continue;
10122                 }
10123                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10124                 }
10125                 var record = new Record(values, id);
10126                 record.json = n;
10127                 records[i] = record;
10128             }
10129             return {
10130             raw : o,
10131                 success : success,
10132                 records : records,
10133                 totalRecords : totalRecords
10134             };
10135     }
10136 });/*
10137  * Based on:
10138  * Ext JS Library 1.1.1
10139  * Copyright(c) 2006-2007, Ext JS, LLC.
10140  *
10141  * Originally Released Under LGPL - original licence link has changed is not relivant.
10142  *
10143  * Fork - LGPL
10144  * <script type="text/javascript">
10145  */
10146
10147 /**
10148  * @class Roo.data.ArrayReader
10149  * @extends Roo.data.DataReader
10150  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10151  * Each element of that Array represents a row of data fields. The
10152  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10153  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10154  * <p>
10155  * Example code:.
10156  * <pre><code>
10157 var RecordDef = Roo.data.Record.create([
10158     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10159     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10160 ]);
10161 var myReader = new Roo.data.ArrayReader({
10162     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10163 }, RecordDef);
10164 </code></pre>
10165  * <p>
10166  * This would consume an Array like this:
10167  * <pre><code>
10168 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10169   </code></pre>
10170  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10171  * @constructor
10172  * Create a new JsonReader
10173  * @param {Object} meta Metadata configuration options.
10174  * @param {Object} recordType Either an Array of field definition objects
10175  * as specified to {@link Roo.data.Record#create},
10176  * or an {@link Roo.data.Record} object
10177  * created using {@link Roo.data.Record#create}.
10178  */
10179 Roo.data.ArrayReader = function(meta, recordType){
10180     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10181 };
10182
10183 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10184     /**
10185      * Create a data block containing Roo.data.Records from an XML document.
10186      * @param {Object} o An Array of row objects which represents the dataset.
10187      * @return {Object} data A data block which is used by an Roo.data.Store object as
10188      * a cache of Roo.data.Records.
10189      */
10190     readRecords : function(o){
10191         var sid = this.meta ? this.meta.id : null;
10192         var recordType = this.recordType, fields = recordType.prototype.fields;
10193         var records = [];
10194         var root = o;
10195             for(var i = 0; i < root.length; i++){
10196                     var n = root[i];
10197                 var values = {};
10198                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10199                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10200                 var f = fields.items[j];
10201                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10202                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10203                 v = f.convert(v);
10204                 values[f.name] = v;
10205             }
10206                 var record = new recordType(values, id);
10207                 record.json = n;
10208                 records[records.length] = record;
10209             }
10210             return {
10211                 records : records,
10212                 totalRecords : records.length
10213             };
10214     }
10215 });/*
10216  * - LGPL
10217  * * 
10218  */
10219
10220 /**
10221  * @class Roo.bootstrap.ComboBox
10222  * @extends Roo.bootstrap.TriggerField
10223  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10224  * @cfg {Boolean} append (true|false) default false
10225  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10226  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10227  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10228  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10229  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10230  * @constructor
10231  * Create a new ComboBox.
10232  * @param {Object} config Configuration options
10233  */
10234 Roo.bootstrap.ComboBox = function(config){
10235     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10236     this.addEvents({
10237         /**
10238          * @event expand
10239          * Fires when the dropdown list is expanded
10240              * @param {Roo.bootstrap.ComboBox} combo This combo box
10241              */
10242         'expand' : true,
10243         /**
10244          * @event collapse
10245          * Fires when the dropdown list is collapsed
10246              * @param {Roo.bootstrap.ComboBox} combo This combo box
10247              */
10248         'collapse' : true,
10249         /**
10250          * @event beforeselect
10251          * Fires before a list item is selected. Return false to cancel the selection.
10252              * @param {Roo.bootstrap.ComboBox} combo This combo box
10253              * @param {Roo.data.Record} record The data record returned from the underlying store
10254              * @param {Number} index The index of the selected item in the dropdown list
10255              */
10256         'beforeselect' : true,
10257         /**
10258          * @event select
10259          * Fires when a list item is selected
10260              * @param {Roo.bootstrap.ComboBox} combo This combo box
10261              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10262              * @param {Number} index The index of the selected item in the dropdown list
10263              */
10264         'select' : true,
10265         /**
10266          * @event beforequery
10267          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10268          * The event object passed has these properties:
10269              * @param {Roo.bootstrap.ComboBox} combo This combo box
10270              * @param {String} query The query
10271              * @param {Boolean} forceAll true to force "all" query
10272              * @param {Boolean} cancel true to cancel the query
10273              * @param {Object} e The query event object
10274              */
10275         'beforequery': true,
10276          /**
10277          * @event add
10278          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10279              * @param {Roo.bootstrap.ComboBox} combo This combo box
10280              */
10281         'add' : true,
10282         /**
10283          * @event edit
10284          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10285              * @param {Roo.bootstrap.ComboBox} combo This combo box
10286              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10287              */
10288         'edit' : true,
10289         /**
10290          * @event remove
10291          * Fires when the remove value from the combobox array
10292              * @param {Roo.bootstrap.ComboBox} combo This combo box
10293              */
10294         'remove' : true
10295         
10296     });
10297     
10298     this.item = [];
10299     this.tickItems = [];
10300     
10301     this.selectedIndex = -1;
10302     if(this.mode == 'local'){
10303         if(config.queryDelay === undefined){
10304             this.queryDelay = 10;
10305         }
10306         if(config.minChars === undefined){
10307             this.minChars = 0;
10308         }
10309     }
10310 };
10311
10312 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10313      
10314     /**
10315      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10316      * rendering into an Roo.Editor, defaults to false)
10317      */
10318     /**
10319      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10320      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10321      */
10322     /**
10323      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10324      */
10325     /**
10326      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10327      * the dropdown list (defaults to undefined, with no header element)
10328      */
10329
10330      /**
10331      * @cfg {String/Roo.Template} tpl The template to use to render the output
10332      */
10333      
10334      /**
10335      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10336      */
10337     listWidth: undefined,
10338     /**
10339      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10340      * mode = 'remote' or 'text' if mode = 'local')
10341      */
10342     displayField: undefined,
10343     /**
10344      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10345      * mode = 'remote' or 'value' if mode = 'local'). 
10346      * Note: use of a valueField requires the user make a selection
10347      * in order for a value to be mapped.
10348      */
10349     valueField: undefined,
10350     
10351     
10352     /**
10353      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10354      * field's data value (defaults to the underlying DOM element's name)
10355      */
10356     hiddenName: undefined,
10357     /**
10358      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10359      */
10360     listClass: '',
10361     /**
10362      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10363      */
10364     selectedClass: 'active',
10365     
10366     /**
10367      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10368      */
10369     shadow:'sides',
10370     /**
10371      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10372      * anchor positions (defaults to 'tl-bl')
10373      */
10374     listAlign: 'tl-bl?',
10375     /**
10376      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10377      */
10378     maxHeight: 300,
10379     /**
10380      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10381      * query specified by the allQuery config option (defaults to 'query')
10382      */
10383     triggerAction: 'query',
10384     /**
10385      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10386      * (defaults to 4, does not apply if editable = false)
10387      */
10388     minChars : 4,
10389     /**
10390      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10391      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10392      */
10393     typeAhead: false,
10394     /**
10395      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10396      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10397      */
10398     queryDelay: 500,
10399     /**
10400      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10401      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10402      */
10403     pageSize: 0,
10404     /**
10405      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10406      * when editable = true (defaults to false)
10407      */
10408     selectOnFocus:false,
10409     /**
10410      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10411      */
10412     queryParam: 'query',
10413     /**
10414      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10415      * when mode = 'remote' (defaults to 'Loading...')
10416      */
10417     loadingText: 'Loading...',
10418     /**
10419      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10420      */
10421     resizable: false,
10422     /**
10423      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10424      */
10425     handleHeight : 8,
10426     /**
10427      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10428      * traditional select (defaults to true)
10429      */
10430     editable: true,
10431     /**
10432      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10433      */
10434     allQuery: '',
10435     /**
10436      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10437      */
10438     mode: 'remote',
10439     /**
10440      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10441      * listWidth has a higher value)
10442      */
10443     minListWidth : 70,
10444     /**
10445      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10446      * allow the user to set arbitrary text into the field (defaults to false)
10447      */
10448     forceSelection:false,
10449     /**
10450      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10451      * if typeAhead = true (defaults to 250)
10452      */
10453     typeAheadDelay : 250,
10454     /**
10455      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10456      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10457      */
10458     valueNotFoundText : undefined,
10459     /**
10460      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10461      */
10462     blockFocus : false,
10463     
10464     /**
10465      * @cfg {Boolean} disableClear Disable showing of clear button.
10466      */
10467     disableClear : false,
10468     /**
10469      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10470      */
10471     alwaysQuery : false,
10472     
10473     /**
10474      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10475      */
10476     multiple : false,
10477     
10478     //private
10479     addicon : false,
10480     editicon: false,
10481     
10482     page: 0,
10483     hasQuery: false,
10484     append: false,
10485     loadNext: false,
10486     autoFocus : true,
10487     tickable : false,
10488     btnPosition : 'right',
10489     triggerList : true,
10490     showToggleBtn : true,
10491     // element that contains real text value.. (when hidden is used..)
10492     
10493     getAutoCreate : function()
10494     {
10495         var cfg = false;
10496         
10497         /*
10498          *  Normal ComboBox
10499          */
10500         if(!this.tickable){
10501             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10502             return cfg;
10503         }
10504         
10505         /*
10506          *  ComboBox with tickable selections
10507          */
10508              
10509         var align = this.labelAlign || this.parentLabelAlign();
10510         
10511         cfg = {
10512             cls : 'form-group roo-combobox-tickable' //input-group
10513         };
10514         
10515         
10516         var buttons = {
10517             tag : 'div',
10518             cls : 'tickable-buttons',
10519             cn : [
10520                 {
10521                     tag : 'button',
10522                     type : 'button',
10523                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10524                     html : 'Edit'
10525                 },
10526                 {
10527                     tag : 'button',
10528                     type : 'button',
10529                     name : 'ok',
10530                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10531                     html : 'Done'
10532                 },
10533                 {
10534                     tag : 'button',
10535                     type : 'button',
10536                     name : 'cancel',
10537                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10538                     html : 'Cancel'
10539                 }
10540             ]
10541         };
10542         
10543         var _this = this;
10544         Roo.each(buttons.cn, function(c){
10545             if (_this.size) {
10546                 c.cls += ' btn-' + _this.size;
10547             }
10548
10549             if (_this.disabled) {
10550                 c.disabled = true;
10551             }
10552         });
10553         
10554         var box = {
10555             tag: 'div',
10556             cn: [
10557                 {
10558                     tag: 'input',
10559                     type : 'hidden',
10560                     cls: 'form-hidden-field'
10561                 },
10562                 {
10563                     tag: 'ul',
10564                     cls: 'select2-choices',
10565                     cn:[
10566                         {
10567                             tag: 'li',
10568                             cls: 'select2-search-field',
10569                             cn: [
10570
10571                                 buttons
10572                             ]
10573                         }
10574                     ]
10575                 }
10576             ]
10577         }
10578         
10579         var combobox = {
10580             cls: 'select2-container input-group select2-container-multi',
10581             cn: [
10582                 box
10583 //                {
10584 //                    tag: 'ul',
10585 //                    cls: 'typeahead typeahead-long dropdown-menu',
10586 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10587 //                }
10588             ]
10589         };
10590         
10591         if (align ==='left' && this.fieldLabel.length) {
10592             
10593                 Roo.log("left and has label");
10594                 cfg.cn = [
10595                     
10596                     {
10597                         tag: 'label',
10598                         'for' :  id,
10599                         cls : 'control-label col-sm-' + this.labelWidth,
10600                         html : this.fieldLabel
10601                         
10602                     },
10603                     {
10604                         cls : "col-sm-" + (12 - this.labelWidth), 
10605                         cn: [
10606                             combobox
10607                         ]
10608                     }
10609                     
10610                 ];
10611         } else if ( this.fieldLabel.length) {
10612                 Roo.log(" label");
10613                  cfg.cn = [
10614                    
10615                     {
10616                         tag: 'label',
10617                         //cls : 'input-group-addon',
10618                         html : this.fieldLabel
10619                         
10620                     },
10621                     
10622                     combobox
10623                     
10624                 ];
10625
10626         } else {
10627             
10628                 Roo.log(" no label && no align");
10629                 cfg = combobox
10630                      
10631                 
10632         }
10633          
10634         var settings=this;
10635         ['xs','sm','md','lg'].map(function(size){
10636             if (settings[size]) {
10637                 cfg.cls += ' col-' + size + '-' + settings[size];
10638             }
10639         });
10640         
10641         return cfg;
10642         
10643     },
10644     
10645     // private
10646     initEvents: function()
10647     {
10648         
10649         if (!this.store) {
10650             throw "can not find store for combo";
10651         }
10652         this.store = Roo.factory(this.store, Roo.data);
10653         
10654         if(this.tickable){
10655             this.initTickableEvents();
10656             return;
10657         }
10658         
10659         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10660         
10661         if(this.hiddenName){
10662             
10663             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10664             
10665             this.hiddenField.dom.value =
10666                 this.hiddenValue !== undefined ? this.hiddenValue :
10667                 this.value !== undefined ? this.value : '';
10668
10669             // prevent input submission
10670             this.el.dom.removeAttribute('name');
10671             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10672              
10673              
10674         }
10675         //if(Roo.isGecko){
10676         //    this.el.dom.setAttribute('autocomplete', 'off');
10677         //}
10678         
10679         var cls = 'x-combo-list';
10680         
10681         //this.list = new Roo.Layer({
10682         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10683         //});
10684         
10685         var _this = this;
10686         
10687         (function(){
10688             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10689             _this.list.setWidth(lw);
10690         }).defer(100);
10691         
10692         this.list.on('mouseover', this.onViewOver, this);
10693         this.list.on('mousemove', this.onViewMove, this);
10694         
10695         this.list.on('scroll', this.onViewScroll, this);
10696         
10697         /*
10698         this.list.swallowEvent('mousewheel');
10699         this.assetHeight = 0;
10700
10701         if(this.title){
10702             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10703             this.assetHeight += this.header.getHeight();
10704         }
10705
10706         this.innerList = this.list.createChild({cls:cls+'-inner'});
10707         this.innerList.on('mouseover', this.onViewOver, this);
10708         this.innerList.on('mousemove', this.onViewMove, this);
10709         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10710         
10711         if(this.allowBlank && !this.pageSize && !this.disableClear){
10712             this.footer = this.list.createChild({cls:cls+'-ft'});
10713             this.pageTb = new Roo.Toolbar(this.footer);
10714            
10715         }
10716         if(this.pageSize){
10717             this.footer = this.list.createChild({cls:cls+'-ft'});
10718             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10719                     {pageSize: this.pageSize});
10720             
10721         }
10722         
10723         if (this.pageTb && this.allowBlank && !this.disableClear) {
10724             var _this = this;
10725             this.pageTb.add(new Roo.Toolbar.Fill(), {
10726                 cls: 'x-btn-icon x-btn-clear',
10727                 text: '&#160;',
10728                 handler: function()
10729                 {
10730                     _this.collapse();
10731                     _this.clearValue();
10732                     _this.onSelect(false, -1);
10733                 }
10734             });
10735         }
10736         if (this.footer) {
10737             this.assetHeight += this.footer.getHeight();
10738         }
10739         */
10740             
10741         if(!this.tpl){
10742             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10743         }
10744
10745         this.view = new Roo.View(this.list, this.tpl, {
10746             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10747         });
10748         //this.view.wrapEl.setDisplayed(false);
10749         this.view.on('click', this.onViewClick, this);
10750         
10751         
10752         
10753         this.store.on('beforeload', this.onBeforeLoad, this);
10754         this.store.on('load', this.onLoad, this);
10755         this.store.on('loadexception', this.onLoadException, this);
10756         /*
10757         if(this.resizable){
10758             this.resizer = new Roo.Resizable(this.list,  {
10759                pinned:true, handles:'se'
10760             });
10761             this.resizer.on('resize', function(r, w, h){
10762                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10763                 this.listWidth = w;
10764                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10765                 this.restrictHeight();
10766             }, this);
10767             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10768         }
10769         */
10770         if(!this.editable){
10771             this.editable = true;
10772             this.setEditable(false);
10773         }
10774         
10775         /*
10776         
10777         if (typeof(this.events.add.listeners) != 'undefined') {
10778             
10779             this.addicon = this.wrap.createChild(
10780                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10781        
10782             this.addicon.on('click', function(e) {
10783                 this.fireEvent('add', this);
10784             }, this);
10785         }
10786         if (typeof(this.events.edit.listeners) != 'undefined') {
10787             
10788             this.editicon = this.wrap.createChild(
10789                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10790             if (this.addicon) {
10791                 this.editicon.setStyle('margin-left', '40px');
10792             }
10793             this.editicon.on('click', function(e) {
10794                 
10795                 // we fire even  if inothing is selected..
10796                 this.fireEvent('edit', this, this.lastData );
10797                 
10798             }, this);
10799         }
10800         */
10801         
10802         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10803             "up" : function(e){
10804                 this.inKeyMode = true;
10805                 this.selectPrev();
10806             },
10807
10808             "down" : function(e){
10809                 if(!this.isExpanded()){
10810                     this.onTriggerClick();
10811                 }else{
10812                     this.inKeyMode = true;
10813                     this.selectNext();
10814                 }
10815             },
10816
10817             "enter" : function(e){
10818 //                this.onViewClick();
10819                 //return true;
10820                 this.collapse();
10821                 
10822                 if(this.fireEvent("specialkey", this, e)){
10823                     this.onViewClick(false);
10824                 }
10825                 
10826                 return true;
10827             },
10828
10829             "esc" : function(e){
10830                 this.collapse();
10831             },
10832
10833             "tab" : function(e){
10834                 this.collapse();
10835                 
10836                 if(this.fireEvent("specialkey", this, e)){
10837                     this.onViewClick(false);
10838                 }
10839                 
10840                 return true;
10841             },
10842
10843             scope : this,
10844
10845             doRelay : function(foo, bar, hname){
10846                 if(hname == 'down' || this.scope.isExpanded()){
10847                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10848                 }
10849                 return true;
10850             },
10851
10852             forceKeyDown: true
10853         });
10854         
10855         
10856         this.queryDelay = Math.max(this.queryDelay || 10,
10857                 this.mode == 'local' ? 10 : 250);
10858         
10859         
10860         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10861         
10862         if(this.typeAhead){
10863             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10864         }
10865         if(this.editable !== false){
10866             this.inputEl().on("keyup", this.onKeyUp, this);
10867         }
10868         if(this.forceSelection){
10869             this.inputEl().on('blur', this.doForce, this);
10870         }
10871         
10872         if(this.multiple){
10873             this.choices = this.el.select('ul.select2-choices', true).first();
10874             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10875         }
10876     },
10877     
10878     initTickableEvents: function()
10879     {   
10880         this.createList();
10881         
10882         if(this.hiddenName){
10883             
10884             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10885             
10886             this.hiddenField.dom.value =
10887                 this.hiddenValue !== undefined ? this.hiddenValue :
10888                 this.value !== undefined ? this.value : '';
10889
10890             // prevent input submission
10891             this.el.dom.removeAttribute('name');
10892             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10893              
10894              
10895         }
10896         
10897 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10898         
10899         this.choices = this.el.select('ul.select2-choices', true).first();
10900         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10901         if(this.triggerList){
10902             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10903         }
10904          
10905         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10906         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10907         
10908         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10909         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10910         
10911         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10912         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10913         
10914         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10915         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10916         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10917         
10918         this.okBtn.hide();
10919         this.cancelBtn.hide();
10920         
10921         var _this = this;
10922         
10923         (function(){
10924             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10925             _this.list.setWidth(lw);
10926         }).defer(100);
10927         
10928         this.list.on('mouseover', this.onViewOver, this);
10929         this.list.on('mousemove', this.onViewMove, this);
10930         
10931         this.list.on('scroll', this.onViewScroll, this);
10932         
10933         if(!this.tpl){
10934             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
10935         }
10936
10937         this.view = new Roo.View(this.list, this.tpl, {
10938             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10939         });
10940         
10941         //this.view.wrapEl.setDisplayed(false);
10942         this.view.on('click', this.onViewClick, this);
10943         
10944         
10945         
10946         this.store.on('beforeload', this.onBeforeLoad, this);
10947         this.store.on('load', this.onLoad, this);
10948         this.store.on('loadexception', this.onLoadException, this);
10949         
10950 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10951 //            "up" : function(e){
10952 //                this.inKeyMode = true;
10953 //                this.selectPrev();
10954 //            },
10955 //
10956 //            "down" : function(e){
10957 //                if(!this.isExpanded()){
10958 //                    this.onTriggerClick();
10959 //                }else{
10960 //                    this.inKeyMode = true;
10961 //                    this.selectNext();
10962 //                }
10963 //            },
10964 //
10965 //            "enter" : function(e){
10966 ////                this.onViewClick();
10967 //                //return true;
10968 //                this.collapse();
10969 //                
10970 //                if(this.fireEvent("specialkey", this, e)){
10971 //                    this.onViewClick(false);
10972 //                }
10973 //                
10974 //                return true;
10975 //            },
10976 //
10977 //            "esc" : function(e){
10978 //                this.collapse();
10979 //            },
10980 //
10981 //            "tab" : function(e){
10982 //                this.collapse();
10983 //                
10984 //                if(this.fireEvent("specialkey", this, e)){
10985 //                    this.onViewClick(false);
10986 //                }
10987 //                
10988 //                return true;
10989 //            },
10990 //
10991 //            scope : this,
10992 //
10993 //            doRelay : function(foo, bar, hname){
10994 //                if(hname == 'down' || this.scope.isExpanded()){
10995 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10996 //                }
10997 //                return true;
10998 //            },
10999 //
11000 //            forceKeyDown: true
11001 //        });
11002         
11003         
11004         this.queryDelay = Math.max(this.queryDelay || 10,
11005                 this.mode == 'local' ? 10 : 250);
11006         
11007         
11008         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11009         
11010         if(this.typeAhead){
11011             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11012         }
11013     },
11014
11015     onDestroy : function(){
11016         if(this.view){
11017             this.view.setStore(null);
11018             this.view.el.removeAllListeners();
11019             this.view.el.remove();
11020             this.view.purgeListeners();
11021         }
11022         if(this.list){
11023             this.list.dom.innerHTML  = '';
11024         }
11025         
11026         if(this.store){
11027             this.store.un('beforeload', this.onBeforeLoad, this);
11028             this.store.un('load', this.onLoad, this);
11029             this.store.un('loadexception', this.onLoadException, this);
11030         }
11031         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11032     },
11033
11034     // private
11035     fireKey : function(e){
11036         if(e.isNavKeyPress() && !this.list.isVisible()){
11037             this.fireEvent("specialkey", this, e);
11038         }
11039     },
11040
11041     // private
11042     onResize: function(w, h){
11043 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11044 //        
11045 //        if(typeof w != 'number'){
11046 //            // we do not handle it!?!?
11047 //            return;
11048 //        }
11049 //        var tw = this.trigger.getWidth();
11050 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11051 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11052 //        var x = w - tw;
11053 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11054 //            
11055 //        //this.trigger.setStyle('left', x+'px');
11056 //        
11057 //        if(this.list && this.listWidth === undefined){
11058 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11059 //            this.list.setWidth(lw);
11060 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11061 //        }
11062         
11063     
11064         
11065     },
11066
11067     /**
11068      * Allow or prevent the user from directly editing the field text.  If false is passed,
11069      * the user will only be able to select from the items defined in the dropdown list.  This method
11070      * is the runtime equivalent of setting the 'editable' config option at config time.
11071      * @param {Boolean} value True to allow the user to directly edit the field text
11072      */
11073     setEditable : function(value){
11074         if(value == this.editable){
11075             return;
11076         }
11077         this.editable = value;
11078         if(!value){
11079             this.inputEl().dom.setAttribute('readOnly', true);
11080             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11081             this.inputEl().addClass('x-combo-noedit');
11082         }else{
11083             this.inputEl().dom.setAttribute('readOnly', false);
11084             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11085             this.inputEl().removeClass('x-combo-noedit');
11086         }
11087     },
11088
11089     // private
11090     
11091     onBeforeLoad : function(combo,opts){
11092         if(!this.hasFocus){
11093             return;
11094         }
11095          if (!opts.add) {
11096             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11097          }
11098 //        this.restrictHeight();
11099         this.selectedIndex = -1;
11100     },
11101
11102     // private
11103     onLoad : function(){
11104         
11105         this.hasQuery = false;
11106         
11107         if(!this.hasFocus){
11108             return;
11109         }
11110         
11111         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11112             this.loading.hide();
11113         }
11114         
11115         if(this.store.getCount() > 0){
11116             this.expand();
11117 //            this.restrictHeight();
11118             if(this.lastQuery == this.allQuery){
11119                 if(this.editable && !this.tickable){
11120                     this.inputEl().dom.select();
11121                 }
11122                 
11123                 if(
11124                     !this.selectByValue(this.value, true) &&
11125                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11126                     this.store.lastOptions.add != true)
11127                 ){
11128                     this.select(0, true);
11129                 }
11130             }else{
11131                 if(this.autoFocus){
11132                     this.selectNext();
11133                 }
11134                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11135                     this.taTask.delay(this.typeAheadDelay);
11136                 }
11137             }
11138         }else{
11139             this.onEmptyResults();
11140         }
11141         
11142         //this.el.focus();
11143     },
11144     // private
11145     onLoadException : function()
11146     {
11147         this.hasQuery = false;
11148         
11149         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11150             this.loading.hide();
11151         }
11152         
11153         this.collapse();
11154         Roo.log(this.store.reader.jsonData);
11155         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11156             // fixme
11157             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11158         }
11159         
11160         
11161     },
11162     // private
11163     onTypeAhead : function(){
11164         if(this.store.getCount() > 0){
11165             var r = this.store.getAt(0);
11166             var newValue = r.data[this.displayField];
11167             var len = newValue.length;
11168             var selStart = this.getRawValue().length;
11169             
11170             if(selStart != len){
11171                 this.setRawValue(newValue);
11172                 this.selectText(selStart, newValue.length);
11173             }
11174         }
11175     },
11176
11177     // private
11178     onSelect : function(record, index){
11179         
11180         if(this.fireEvent('beforeselect', this, record, index) !== false){
11181         
11182             this.setFromData(index > -1 ? record.data : false);
11183             
11184             this.collapse();
11185             this.fireEvent('select', this, record, index);
11186         }
11187     },
11188
11189     /**
11190      * Returns the currently selected field value or empty string if no value is set.
11191      * @return {String} value The selected value
11192      */
11193     getValue : function(){
11194         
11195         if(this.multiple){
11196             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11197         }
11198         
11199         if(this.valueField){
11200             return typeof this.value != 'undefined' ? this.value : '';
11201         }else{
11202             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11203         }
11204     },
11205
11206     /**
11207      * Clears any text/value currently set in the field
11208      */
11209     clearValue : function(){
11210         if(this.hiddenField){
11211             this.hiddenField.dom.value = '';
11212         }
11213         this.value = '';
11214         this.setRawValue('');
11215         this.lastSelectionText = '';
11216         
11217     },
11218
11219     /**
11220      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11221      * will be displayed in the field.  If the value does not match the data value of an existing item,
11222      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11223      * Otherwise the field will be blank (although the value will still be set).
11224      * @param {String} value The value to match
11225      */
11226     setValue : function(v){
11227         if(this.multiple){
11228             this.syncValue();
11229             return;
11230         }
11231         
11232         var text = v;
11233         if(this.valueField){
11234             var r = this.findRecord(this.valueField, v);
11235             if(r){
11236                 text = r.data[this.displayField];
11237             }else if(this.valueNotFoundText !== undefined){
11238                 text = this.valueNotFoundText;
11239             }
11240         }
11241         this.lastSelectionText = text;
11242         if(this.hiddenField){
11243             this.hiddenField.dom.value = v;
11244         }
11245         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11246         this.value = v;
11247     },
11248     /**
11249      * @property {Object} the last set data for the element
11250      */
11251     
11252     lastData : false,
11253     /**
11254      * Sets the value of the field based on a object which is related to the record format for the store.
11255      * @param {Object} value the value to set as. or false on reset?
11256      */
11257     setFromData : function(o){
11258         
11259         if(this.multiple){
11260             if(typeof o.display_name !== 'string'){
11261                 for(var i=0;i<o.display_name.length;i++){
11262                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11263                 }
11264                 return;
11265             }
11266             this.addItem(o);
11267             return;
11268         }
11269             
11270         var dv = ''; // display value
11271         var vv = ''; // value value..
11272         this.lastData = o;
11273         if (this.displayField) {
11274             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11275         } else {
11276             // this is an error condition!!!
11277             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11278         }
11279         
11280         if(this.valueField){
11281             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11282         }
11283         
11284         if(this.hiddenField){
11285             this.hiddenField.dom.value = vv;
11286             
11287             this.lastSelectionText = dv;
11288             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11289             this.value = vv;
11290             return;
11291         }
11292         // no hidden field.. - we store the value in 'value', but still display
11293         // display field!!!!
11294         this.lastSelectionText = dv;
11295         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11296         this.value = vv;
11297         
11298         
11299     },
11300     // private
11301     reset : function(){
11302         // overridden so that last data is reset..
11303         this.setValue(this.originalValue);
11304         this.clearInvalid();
11305         this.lastData = false;
11306         if (this.view) {
11307             this.view.clearSelections();
11308         }
11309     },
11310     // private
11311     findRecord : function(prop, value){
11312         var record;
11313         if(this.store.getCount() > 0){
11314             this.store.each(function(r){
11315                 if(r.data[prop] == value){
11316                     record = r;
11317                     return false;
11318                 }
11319                 return true;
11320             });
11321         }
11322         return record;
11323     },
11324     
11325     getName: function()
11326     {
11327         // returns hidden if it's set..
11328         if (!this.rendered) {return ''};
11329         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11330         
11331     },
11332     // private
11333     onViewMove : function(e, t){
11334         this.inKeyMode = false;
11335     },
11336
11337     // private
11338     onViewOver : function(e, t){
11339         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11340             return;
11341         }
11342         var item = this.view.findItemFromChild(t);
11343         
11344         if(item){
11345             var index = this.view.indexOf(item);
11346             this.select(index, false);
11347         }
11348     },
11349
11350     // private
11351     onViewClick : function(view, doFocus, el, e)
11352     {
11353         var index = this.view.getSelectedIndexes()[0];
11354         
11355         var r = this.store.getAt(index);
11356         
11357         if(this.tickable){
11358             
11359             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11360                 return;
11361             }
11362             
11363             var rm = false;
11364             var _this = this;
11365             
11366             Roo.each(this.tickItems, function(v,k){
11367                 
11368                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11369                     _this.tickItems.splice(k, 1);
11370                     rm = true;
11371                     return;
11372                 }
11373             })
11374             
11375             if(rm){
11376                 return;
11377             }
11378             
11379             this.tickItems.push(r.data);
11380             return;
11381         }
11382         
11383         if(r){
11384             this.onSelect(r, index);
11385         }
11386         if(doFocus !== false && !this.blockFocus){
11387             this.inputEl().focus();
11388         }
11389     },
11390
11391     // private
11392     restrictHeight : function(){
11393         //this.innerList.dom.style.height = '';
11394         //var inner = this.innerList.dom;
11395         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11396         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11397         //this.list.beginUpdate();
11398         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11399         this.list.alignTo(this.inputEl(), this.listAlign);
11400         this.list.alignTo(this.inputEl(), this.listAlign);
11401         //this.list.endUpdate();
11402     },
11403
11404     // private
11405     onEmptyResults : function(){
11406         this.collapse();
11407     },
11408
11409     /**
11410      * Returns true if the dropdown list is expanded, else false.
11411      */
11412     isExpanded : function(){
11413         return this.list.isVisible();
11414     },
11415
11416     /**
11417      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11418      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11419      * @param {String} value The data value of the item to select
11420      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11421      * selected item if it is not currently in view (defaults to true)
11422      * @return {Boolean} True if the value matched an item in the list, else false
11423      */
11424     selectByValue : function(v, scrollIntoView){
11425         if(v !== undefined && v !== null){
11426             var r = this.findRecord(this.valueField || this.displayField, v);
11427             if(r){
11428                 this.select(this.store.indexOf(r), scrollIntoView);
11429                 return true;
11430             }
11431         }
11432         return false;
11433     },
11434
11435     /**
11436      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11437      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11438      * @param {Number} index The zero-based index of the list item to select
11439      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11440      * selected item if it is not currently in view (defaults to true)
11441      */
11442     select : function(index, scrollIntoView){
11443         this.selectedIndex = index;
11444         this.view.select(index);
11445         if(scrollIntoView !== false){
11446             var el = this.view.getNode(index);
11447             if(el && !this.multiple && !this.tickable){
11448                 this.list.scrollChildIntoView(el, false);
11449             }
11450         }
11451     },
11452
11453     // private
11454     selectNext : function(){
11455         var ct = this.store.getCount();
11456         if(ct > 0){
11457             if(this.selectedIndex == -1){
11458                 this.select(0);
11459             }else if(this.selectedIndex < ct-1){
11460                 this.select(this.selectedIndex+1);
11461             }
11462         }
11463     },
11464
11465     // private
11466     selectPrev : function(){
11467         var ct = this.store.getCount();
11468         if(ct > 0){
11469             if(this.selectedIndex == -1){
11470                 this.select(0);
11471             }else if(this.selectedIndex != 0){
11472                 this.select(this.selectedIndex-1);
11473             }
11474         }
11475     },
11476
11477     // private
11478     onKeyUp : function(e){
11479         if(this.editable !== false && !e.isSpecialKey()){
11480             this.lastKey = e.getKey();
11481             this.dqTask.delay(this.queryDelay);
11482         }
11483     },
11484
11485     // private
11486     validateBlur : function(){
11487         return !this.list || !this.list.isVisible();   
11488     },
11489
11490     // private
11491     initQuery : function(){
11492         this.doQuery(this.getRawValue());
11493     },
11494
11495     // private
11496     doForce : function(){
11497         if(this.inputEl().dom.value.length > 0){
11498             this.inputEl().dom.value =
11499                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11500              
11501         }
11502     },
11503
11504     /**
11505      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11506      * query allowing the query action to be canceled if needed.
11507      * @param {String} query The SQL query to execute
11508      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11509      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11510      * saved in the current store (defaults to false)
11511      */
11512     doQuery : function(q, forceAll){
11513         
11514         if(q === undefined || q === null){
11515             q = '';
11516         }
11517         var qe = {
11518             query: q,
11519             forceAll: forceAll,
11520             combo: this,
11521             cancel:false
11522         };
11523         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11524             return false;
11525         }
11526         q = qe.query;
11527         
11528         forceAll = qe.forceAll;
11529         if(forceAll === true || (q.length >= this.minChars)){
11530             
11531             this.hasQuery = true;
11532             
11533             if(this.lastQuery != q || this.alwaysQuery){
11534                 this.lastQuery = q;
11535                 if(this.mode == 'local'){
11536                     this.selectedIndex = -1;
11537                     if(forceAll){
11538                         this.store.clearFilter();
11539                     }else{
11540                         this.store.filter(this.displayField, q);
11541                     }
11542                     this.onLoad();
11543                 }else{
11544                     this.store.baseParams[this.queryParam] = q;
11545                     
11546                     var options = {params : this.getParams(q)};
11547                     
11548                     if(this.loadNext){
11549                         options.add = true;
11550                         options.params.start = this.page * this.pageSize;
11551                     }
11552                     
11553                     this.store.load(options);
11554                     /*
11555                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11556                      *  we should expand the list on onLoad
11557                      *  so command out it
11558                      */
11559 //                    this.expand();
11560                 }
11561             }else{
11562                 this.selectedIndex = -1;
11563                 this.onLoad();   
11564             }
11565         }
11566         
11567         this.loadNext = false;
11568     },
11569
11570     // private
11571     getParams : function(q){
11572         var p = {};
11573         //p[this.queryParam] = q;
11574         
11575         if(this.pageSize){
11576             p.start = 0;
11577             p.limit = this.pageSize;
11578         }
11579         return p;
11580     },
11581
11582     /**
11583      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11584      */
11585     collapse : function(){
11586         if(!this.isExpanded()){
11587             return;
11588         }
11589         
11590         this.list.hide();
11591         
11592         if(this.tickable){
11593             this.okBtn.hide();
11594             this.cancelBtn.hide();
11595             this.trigger.show();
11596         }
11597         
11598         Roo.get(document).un('mousedown', this.collapseIf, this);
11599         Roo.get(document).un('mousewheel', this.collapseIf, this);
11600         if (!this.editable) {
11601             Roo.get(document).un('keydown', this.listKeyPress, this);
11602         }
11603         this.fireEvent('collapse', this);
11604     },
11605
11606     // private
11607     collapseIf : function(e){
11608         var in_combo  = e.within(this.el);
11609         var in_list =  e.within(this.list);
11610         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11611         
11612         if (in_combo || in_list || is_list) {
11613             //e.stopPropagation();
11614             return;
11615         }
11616         
11617         if(this.tickable){
11618             this.onTickableFooterButtonClick(e, false, false);
11619         }
11620
11621         this.collapse();
11622         
11623     },
11624
11625     /**
11626      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11627      */
11628     expand : function(){
11629        
11630         if(this.isExpanded() || !this.hasFocus){
11631             return;
11632         }
11633         
11634         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11635         this.list.setWidth(lw);
11636         
11637         
11638          Roo.log('expand');
11639         
11640         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11641         this.list.setWidth(lw);
11642             
11643         this.list.show();
11644         
11645         this.restrictHeight();
11646         
11647         if(this.tickable){
11648             
11649             this.tickItems = Roo.apply([], this.item);
11650             
11651             this.okBtn.show();
11652             this.cancelBtn.show();
11653             this.trigger.hide();
11654             
11655         }
11656         
11657         Roo.get(document).on('mousedown', this.collapseIf, this);
11658         Roo.get(document).on('mousewheel', this.collapseIf, this);
11659         if (!this.editable) {
11660             Roo.get(document).on('keydown', this.listKeyPress, this);
11661         }
11662         
11663         this.fireEvent('expand', this);
11664     },
11665
11666     // private
11667     // Implements the default empty TriggerField.onTriggerClick function
11668     onTriggerClick : function(e)
11669     {
11670         Roo.log('trigger click');
11671         
11672         if(this.disabled || !this.triggerList){
11673             return;
11674         }
11675         
11676         this.page = 0;
11677         this.loadNext = false;
11678         
11679         if(this.isExpanded()){
11680             this.collapse();
11681             if (!this.blockFocus) {
11682                 this.inputEl().focus();
11683             }
11684             
11685         }else {
11686             this.hasFocus = true;
11687             if(this.triggerAction == 'all') {
11688                 this.doQuery(this.allQuery, true);
11689             } else {
11690                 this.doQuery(this.getRawValue());
11691             }
11692             if (!this.blockFocus) {
11693                 this.inputEl().focus();
11694             }
11695         }
11696     },
11697     
11698     onTickableTriggerClick : function(e)
11699     {
11700         if(this.disabled){
11701             return;
11702         }
11703         
11704         this.page = 0;
11705         this.loadNext = false;
11706         this.hasFocus = true;
11707         
11708         if(this.triggerAction == 'all') {
11709             this.doQuery(this.allQuery, true);
11710         } else {
11711             this.doQuery(this.getRawValue());
11712         }
11713     },
11714     
11715     onSearchFieldClick : function(e)
11716     {
11717         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11718             return;
11719         }
11720         
11721         this.page = 0;
11722         this.loadNext = false;
11723         this.hasFocus = true;
11724         
11725         if(this.triggerAction == 'all') {
11726             this.doQuery(this.allQuery, true);
11727         } else {
11728             this.doQuery(this.getRawValue());
11729         }
11730     },
11731     
11732     listKeyPress : function(e)
11733     {
11734         //Roo.log('listkeypress');
11735         // scroll to first matching element based on key pres..
11736         if (e.isSpecialKey()) {
11737             return false;
11738         }
11739         var k = String.fromCharCode(e.getKey()).toUpperCase();
11740         //Roo.log(k);
11741         var match  = false;
11742         var csel = this.view.getSelectedNodes();
11743         var cselitem = false;
11744         if (csel.length) {
11745             var ix = this.view.indexOf(csel[0]);
11746             cselitem  = this.store.getAt(ix);
11747             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11748                 cselitem = false;
11749             }
11750             
11751         }
11752         
11753         this.store.each(function(v) { 
11754             if (cselitem) {
11755                 // start at existing selection.
11756                 if (cselitem.id == v.id) {
11757                     cselitem = false;
11758                 }
11759                 return true;
11760             }
11761                 
11762             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11763                 match = this.store.indexOf(v);
11764                 return false;
11765             }
11766             return true;
11767         }, this);
11768         
11769         if (match === false) {
11770             return true; // no more action?
11771         }
11772         // scroll to?
11773         this.view.select(match);
11774         var sn = Roo.get(this.view.getSelectedNodes()[0])
11775         //sn.scrollIntoView(sn.dom.parentNode, false);
11776     },
11777     
11778     onViewScroll : function(e, t){
11779         
11780         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11781             return;
11782         }
11783         
11784         this.hasQuery = true;
11785         
11786         this.loading = this.list.select('.loading', true).first();
11787         
11788         if(this.loading === null){
11789             this.list.createChild({
11790                 tag: 'div',
11791                 cls: 'loading select2-more-results select2-active',
11792                 html: 'Loading more results...'
11793             })
11794             
11795             this.loading = this.list.select('.loading', true).first();
11796             
11797             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11798             
11799             this.loading.hide();
11800         }
11801         
11802         this.loading.show();
11803         
11804         var _combo = this;
11805         
11806         this.page++;
11807         this.loadNext = true;
11808         
11809         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11810         
11811         return;
11812     },
11813     
11814     addItem : function(o)
11815     {   
11816         var dv = ''; // display value
11817         
11818         if (this.displayField) {
11819             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11820         } else {
11821             // this is an error condition!!!
11822             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11823         }
11824         
11825         if(!dv.length){
11826             return;
11827         }
11828         
11829         var choice = this.choices.createChild({
11830             tag: 'li',
11831             cls: 'select2-search-choice',
11832             cn: [
11833                 {
11834                     tag: 'div',
11835                     html: dv
11836                 },
11837                 {
11838                     tag: 'a',
11839                     href: '#',
11840                     cls: 'select2-search-choice-close',
11841                     tabindex: '-1'
11842                 }
11843             ]
11844             
11845         }, this.searchField);
11846         
11847         var close = choice.select('a.select2-search-choice-close', true).first()
11848         
11849         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11850         
11851         this.item.push(o);
11852         
11853         this.lastData = o;
11854         
11855         this.syncValue();
11856         
11857         this.inputEl().dom.value = '';
11858         
11859     },
11860     
11861     onRemoveItem : function(e, _self, o)
11862     {
11863         e.preventDefault();
11864         var index = this.item.indexOf(o.data) * 1;
11865         
11866         if( index < 0){
11867             Roo.log('not this item?!');
11868             return;
11869         }
11870         
11871         this.item.splice(index, 1);
11872         o.item.remove();
11873         
11874         this.syncValue();
11875         
11876         this.fireEvent('remove', this, e);
11877         
11878     },
11879     
11880     syncValue : function()
11881     {
11882         if(!this.item.length){
11883             this.clearValue();
11884             return;
11885         }
11886             
11887         var value = [];
11888         var _this = this;
11889         Roo.each(this.item, function(i){
11890             if(_this.valueField){
11891                 value.push(i[_this.valueField]);
11892                 return;
11893             }
11894
11895             value.push(i);
11896         });
11897
11898         this.value = value.join(',');
11899
11900         if(this.hiddenField){
11901             this.hiddenField.dom.value = this.value;
11902         }
11903     },
11904     
11905     clearItem : function()
11906     {
11907         if(!this.multiple){
11908             return;
11909         }
11910         
11911         this.item = [];
11912         
11913         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11914            c.remove();
11915         });
11916         
11917         this.syncValue();
11918     },
11919     
11920     inputEl: function ()
11921     {
11922         if(this.tickable){
11923             return this.searchField;
11924         }
11925         return this.el.select('input.form-control',true).first();
11926     },
11927     
11928     
11929     onTickableFooterButtonClick : function(e, btn, el)
11930     {
11931         e.preventDefault();
11932         
11933         if(btn && btn.name == 'cancel'){
11934             this.tickItems = Roo.apply([], this.item);
11935             this.collapse();
11936             return;
11937         }
11938         
11939         this.clearItem();
11940         
11941         var _this = this;
11942         
11943         Roo.each(this.tickItems, function(o){
11944             _this.addItem(o);
11945         });
11946         
11947         this.collapse();
11948         
11949     }
11950     
11951     
11952
11953     /** 
11954     * @cfg {Boolean} grow 
11955     * @hide 
11956     */
11957     /** 
11958     * @cfg {Number} growMin 
11959     * @hide 
11960     */
11961     /** 
11962     * @cfg {Number} growMax 
11963     * @hide 
11964     */
11965     /**
11966      * @hide
11967      * @method autoSize
11968      */
11969 });
11970 /*
11971  * Based on:
11972  * Ext JS Library 1.1.1
11973  * Copyright(c) 2006-2007, Ext JS, LLC.
11974  *
11975  * Originally Released Under LGPL - original licence link has changed is not relivant.
11976  *
11977  * Fork - LGPL
11978  * <script type="text/javascript">
11979  */
11980
11981 /**
11982  * @class Roo.View
11983  * @extends Roo.util.Observable
11984  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11985  * This class also supports single and multi selection modes. <br>
11986  * Create a data model bound view:
11987  <pre><code>
11988  var store = new Roo.data.Store(...);
11989
11990  var view = new Roo.View({
11991     el : "my-element",
11992     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11993  
11994     singleSelect: true,
11995     selectedClass: "ydataview-selected",
11996     store: store
11997  });
11998
11999  // listen for node click?
12000  view.on("click", function(vw, index, node, e){
12001  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12002  });
12003
12004  // load XML data
12005  dataModel.load("foobar.xml");
12006  </code></pre>
12007  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12008  * <br><br>
12009  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12010  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12011  * 
12012  * Note: old style constructor is still suported (container, template, config)
12013  * 
12014  * @constructor
12015  * Create a new View
12016  * @param {Object} config The config object
12017  * 
12018  */
12019 Roo.View = function(config, depreciated_tpl, depreciated_config){
12020     
12021     this.parent = false;
12022     
12023     if (typeof(depreciated_tpl) == 'undefined') {
12024         // new way.. - universal constructor.
12025         Roo.apply(this, config);
12026         this.el  = Roo.get(this.el);
12027     } else {
12028         // old format..
12029         this.el  = Roo.get(config);
12030         this.tpl = depreciated_tpl;
12031         Roo.apply(this, depreciated_config);
12032     }
12033     this.wrapEl  = this.el.wrap().wrap();
12034     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12035     
12036     
12037     if(typeof(this.tpl) == "string"){
12038         this.tpl = new Roo.Template(this.tpl);
12039     } else {
12040         // support xtype ctors..
12041         this.tpl = new Roo.factory(this.tpl, Roo);
12042     }
12043     
12044     
12045     this.tpl.compile();
12046     
12047     /** @private */
12048     this.addEvents({
12049         /**
12050          * @event beforeclick
12051          * Fires before a click is processed. Returns false to cancel the default action.
12052          * @param {Roo.View} this
12053          * @param {Number} index The index of the target node
12054          * @param {HTMLElement} node The target node
12055          * @param {Roo.EventObject} e The raw event object
12056          */
12057             "beforeclick" : true,
12058         /**
12059          * @event click
12060          * Fires when a template node is clicked.
12061          * @param {Roo.View} this
12062          * @param {Number} index The index of the target node
12063          * @param {HTMLElement} node The target node
12064          * @param {Roo.EventObject} e The raw event object
12065          */
12066             "click" : true,
12067         /**
12068          * @event dblclick
12069          * Fires when a template node is double clicked.
12070          * @param {Roo.View} this
12071          * @param {Number} index The index of the target node
12072          * @param {HTMLElement} node The target node
12073          * @param {Roo.EventObject} e The raw event object
12074          */
12075             "dblclick" : true,
12076         /**
12077          * @event contextmenu
12078          * Fires when a template node is right clicked.
12079          * @param {Roo.View} this
12080          * @param {Number} index The index of the target node
12081          * @param {HTMLElement} node The target node
12082          * @param {Roo.EventObject} e The raw event object
12083          */
12084             "contextmenu" : true,
12085         /**
12086          * @event selectionchange
12087          * Fires when the selected nodes change.
12088          * @param {Roo.View} this
12089          * @param {Array} selections Array of the selected nodes
12090          */
12091             "selectionchange" : true,
12092     
12093         /**
12094          * @event beforeselect
12095          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12096          * @param {Roo.View} this
12097          * @param {HTMLElement} node The node to be selected
12098          * @param {Array} selections Array of currently selected nodes
12099          */
12100             "beforeselect" : true,
12101         /**
12102          * @event preparedata
12103          * Fires on every row to render, to allow you to change the data.
12104          * @param {Roo.View} this
12105          * @param {Object} data to be rendered (change this)
12106          */
12107           "preparedata" : true
12108           
12109           
12110         });
12111
12112
12113
12114     this.el.on({
12115         "click": this.onClick,
12116         "dblclick": this.onDblClick,
12117         "contextmenu": this.onContextMenu,
12118         scope:this
12119     });
12120
12121     this.selections = [];
12122     this.nodes = [];
12123     this.cmp = new Roo.CompositeElementLite([]);
12124     if(this.store){
12125         this.store = Roo.factory(this.store, Roo.data);
12126         this.setStore(this.store, true);
12127     }
12128     
12129     if ( this.footer && this.footer.xtype) {
12130            
12131          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12132         
12133         this.footer.dataSource = this.store
12134         this.footer.container = fctr;
12135         this.footer = Roo.factory(this.footer, Roo);
12136         fctr.insertFirst(this.el);
12137         
12138         // this is a bit insane - as the paging toolbar seems to detach the el..
12139 //        dom.parentNode.parentNode.parentNode
12140          // they get detached?
12141     }
12142     
12143     
12144     Roo.View.superclass.constructor.call(this);
12145     
12146     
12147 };
12148
12149 Roo.extend(Roo.View, Roo.util.Observable, {
12150     
12151      /**
12152      * @cfg {Roo.data.Store} store Data store to load data from.
12153      */
12154     store : false,
12155     
12156     /**
12157      * @cfg {String|Roo.Element} el The container element.
12158      */
12159     el : '',
12160     
12161     /**
12162      * @cfg {String|Roo.Template} tpl The template used by this View 
12163      */
12164     tpl : false,
12165     /**
12166      * @cfg {String} dataName the named area of the template to use as the data area
12167      *                          Works with domtemplates roo-name="name"
12168      */
12169     dataName: false,
12170     /**
12171      * @cfg {String} selectedClass The css class to add to selected nodes
12172      */
12173     selectedClass : "x-view-selected",
12174      /**
12175      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12176      */
12177     emptyText : "",
12178     
12179     /**
12180      * @cfg {String} text to display on mask (default Loading)
12181      */
12182     mask : false,
12183     /**
12184      * @cfg {Boolean} multiSelect Allow multiple selection
12185      */
12186     multiSelect : false,
12187     /**
12188      * @cfg {Boolean} singleSelect Allow single selection
12189      */
12190     singleSelect:  false,
12191     
12192     /**
12193      * @cfg {Boolean} toggleSelect - selecting 
12194      */
12195     toggleSelect : false,
12196     
12197     /**
12198      * @cfg {Boolean} tickable - selecting 
12199      */
12200     tickable : false,
12201     
12202     /**
12203      * Returns the element this view is bound to.
12204      * @return {Roo.Element}
12205      */
12206     getEl : function(){
12207         return this.wrapEl;
12208     },
12209     
12210     
12211
12212     /**
12213      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12214      */
12215     refresh : function(){
12216         Roo.log('refresh');
12217         var t = this.tpl;
12218         
12219         // if we are using something like 'domtemplate', then
12220         // the what gets used is:
12221         // t.applySubtemplate(NAME, data, wrapping data..)
12222         // the outer template then get' applied with
12223         //     the store 'extra data'
12224         // and the body get's added to the
12225         //      roo-name="data" node?
12226         //      <span class='roo-tpl-{name}'></span> ?????
12227         
12228         
12229         
12230         this.clearSelections();
12231         this.el.update("");
12232         var html = [];
12233         var records = this.store.getRange();
12234         if(records.length < 1) {
12235             
12236             // is this valid??  = should it render a template??
12237             
12238             this.el.update(this.emptyText);
12239             return;
12240         }
12241         var el = this.el;
12242         if (this.dataName) {
12243             this.el.update(t.apply(this.store.meta)); //????
12244             el = this.el.child('.roo-tpl-' + this.dataName);
12245         }
12246         
12247         for(var i = 0, len = records.length; i < len; i++){
12248             var data = this.prepareData(records[i].data, i, records[i]);
12249             this.fireEvent("preparedata", this, data, i, records[i]);
12250             
12251             var d = Roo.apply({}, data);
12252             
12253             if(this.tickable){
12254                 Roo.apply(d, {'roo-id' : Roo.id()});
12255                 
12256                 var _this = this;
12257             
12258                 Roo.each(this.parent.item, function(item){
12259                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12260                         return;
12261                     }
12262                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12263                 });
12264             }
12265             
12266             html[html.length] = Roo.util.Format.trim(
12267                 this.dataName ?
12268                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12269                     t.apply(d)
12270             );
12271         }
12272         
12273         
12274         
12275         el.update(html.join(""));
12276         this.nodes = el.dom.childNodes;
12277         this.updateIndexes(0);
12278     },
12279     
12280
12281     /**
12282      * Function to override to reformat the data that is sent to
12283      * the template for each node.
12284      * DEPRICATED - use the preparedata event handler.
12285      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12286      * a JSON object for an UpdateManager bound view).
12287      */
12288     prepareData : function(data, index, record)
12289     {
12290         this.fireEvent("preparedata", this, data, index, record);
12291         return data;
12292     },
12293
12294     onUpdate : function(ds, record){
12295          Roo.log('on update');   
12296         this.clearSelections();
12297         var index = this.store.indexOf(record);
12298         var n = this.nodes[index];
12299         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12300         n.parentNode.removeChild(n);
12301         this.updateIndexes(index, index);
12302     },
12303
12304     
12305     
12306 // --------- FIXME     
12307     onAdd : function(ds, records, index)
12308     {
12309         Roo.log(['on Add', ds, records, index] );        
12310         this.clearSelections();
12311         if(this.nodes.length == 0){
12312             this.refresh();
12313             return;
12314         }
12315         var n = this.nodes[index];
12316         for(var i = 0, len = records.length; i < len; i++){
12317             var d = this.prepareData(records[i].data, i, records[i]);
12318             if(n){
12319                 this.tpl.insertBefore(n, d);
12320             }else{
12321                 
12322                 this.tpl.append(this.el, d);
12323             }
12324         }
12325         this.updateIndexes(index);
12326     },
12327
12328     onRemove : function(ds, record, index){
12329         Roo.log('onRemove');
12330         this.clearSelections();
12331         var el = this.dataName  ?
12332             this.el.child('.roo-tpl-' + this.dataName) :
12333             this.el; 
12334         
12335         el.dom.removeChild(this.nodes[index]);
12336         this.updateIndexes(index);
12337     },
12338
12339     /**
12340      * Refresh an individual node.
12341      * @param {Number} index
12342      */
12343     refreshNode : function(index){
12344         this.onUpdate(this.store, this.store.getAt(index));
12345     },
12346
12347     updateIndexes : function(startIndex, endIndex){
12348         var ns = this.nodes;
12349         startIndex = startIndex || 0;
12350         endIndex = endIndex || ns.length - 1;
12351         for(var i = startIndex; i <= endIndex; i++){
12352             ns[i].nodeIndex = i;
12353         }
12354     },
12355
12356     /**
12357      * Changes the data store this view uses and refresh the view.
12358      * @param {Store} store
12359      */
12360     setStore : function(store, initial){
12361         if(!initial && this.store){
12362             this.store.un("datachanged", this.refresh);
12363             this.store.un("add", this.onAdd);
12364             this.store.un("remove", this.onRemove);
12365             this.store.un("update", this.onUpdate);
12366             this.store.un("clear", this.refresh);
12367             this.store.un("beforeload", this.onBeforeLoad);
12368             this.store.un("load", this.onLoad);
12369             this.store.un("loadexception", this.onLoad);
12370         }
12371         if(store){
12372           
12373             store.on("datachanged", this.refresh, this);
12374             store.on("add", this.onAdd, this);
12375             store.on("remove", this.onRemove, this);
12376             store.on("update", this.onUpdate, this);
12377             store.on("clear", this.refresh, this);
12378             store.on("beforeload", this.onBeforeLoad, this);
12379             store.on("load", this.onLoad, this);
12380             store.on("loadexception", this.onLoad, this);
12381         }
12382         
12383         if(store){
12384             this.refresh();
12385         }
12386     },
12387     /**
12388      * onbeforeLoad - masks the loading area.
12389      *
12390      */
12391     onBeforeLoad : function(store,opts)
12392     {
12393          Roo.log('onBeforeLoad');   
12394         if (!opts.add) {
12395             this.el.update("");
12396         }
12397         this.el.mask(this.mask ? this.mask : "Loading" ); 
12398     },
12399     onLoad : function ()
12400     {
12401         this.el.unmask();
12402     },
12403     
12404
12405     /**
12406      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12407      * @param {HTMLElement} node
12408      * @return {HTMLElement} The template node
12409      */
12410     findItemFromChild : function(node){
12411         var el = this.dataName  ?
12412             this.el.child('.roo-tpl-' + this.dataName,true) :
12413             this.el.dom; 
12414         
12415         if(!node || node.parentNode == el){
12416                     return node;
12417             }
12418             var p = node.parentNode;
12419             while(p && p != el){
12420             if(p.parentNode == el){
12421                 return p;
12422             }
12423             p = p.parentNode;
12424         }
12425             return null;
12426     },
12427
12428     /** @ignore */
12429     onClick : function(e){
12430         var item = this.findItemFromChild(e.getTarget());
12431         if(item){
12432             var index = this.indexOf(item);
12433             if(this.onItemClick(item, index, e) !== false){
12434                 this.fireEvent("click", this, index, item, e);
12435             }
12436         }else{
12437             this.clearSelections();
12438         }
12439     },
12440
12441     /** @ignore */
12442     onContextMenu : function(e){
12443         var item = this.findItemFromChild(e.getTarget());
12444         if(item){
12445             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12446         }
12447     },
12448
12449     /** @ignore */
12450     onDblClick : function(e){
12451         var item = this.findItemFromChild(e.getTarget());
12452         if(item){
12453             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12454         }
12455     },
12456
12457     onItemClick : function(item, index, e)
12458     {
12459         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12460             return false;
12461         }
12462         if (this.toggleSelect) {
12463             var m = this.isSelected(item) ? 'unselect' : 'select';
12464             Roo.log(m);
12465             var _t = this;
12466             _t[m](item, true, false);
12467             return true;
12468         }
12469         if(this.multiSelect || this.singleSelect){
12470             if(this.multiSelect && e.shiftKey && this.lastSelection){
12471                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12472             }else{
12473                 this.select(item, this.multiSelect && e.ctrlKey);
12474                 this.lastSelection = item;
12475             }
12476             
12477             if(!this.tickable){
12478                 e.preventDefault();
12479             }
12480             
12481         }
12482         return true;
12483     },
12484
12485     /**
12486      * Get the number of selected nodes.
12487      * @return {Number}
12488      */
12489     getSelectionCount : function(){
12490         return this.selections.length;
12491     },
12492
12493     /**
12494      * Get the currently selected nodes.
12495      * @return {Array} An array of HTMLElements
12496      */
12497     getSelectedNodes : function(){
12498         return this.selections;
12499     },
12500
12501     /**
12502      * Get the indexes of the selected nodes.
12503      * @return {Array}
12504      */
12505     getSelectedIndexes : function(){
12506         var indexes = [], s = this.selections;
12507         for(var i = 0, len = s.length; i < len; i++){
12508             indexes.push(s[i].nodeIndex);
12509         }
12510         return indexes;
12511     },
12512
12513     /**
12514      * Clear all selections
12515      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12516      */
12517     clearSelections : function(suppressEvent){
12518         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12519             this.cmp.elements = this.selections;
12520             this.cmp.removeClass(this.selectedClass);
12521             this.selections = [];
12522             if(!suppressEvent){
12523                 this.fireEvent("selectionchange", this, this.selections);
12524             }
12525         }
12526     },
12527
12528     /**
12529      * Returns true if the passed node is selected
12530      * @param {HTMLElement/Number} node The node or node index
12531      * @return {Boolean}
12532      */
12533     isSelected : function(node){
12534         var s = this.selections;
12535         if(s.length < 1){
12536             return false;
12537         }
12538         node = this.getNode(node);
12539         return s.indexOf(node) !== -1;
12540     },
12541
12542     /**
12543      * Selects nodes.
12544      * @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
12545      * @param {Boolean} keepExisting (optional) true to keep existing selections
12546      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12547      */
12548     select : function(nodeInfo, keepExisting, suppressEvent){
12549         if(nodeInfo instanceof Array){
12550             if(!keepExisting){
12551                 this.clearSelections(true);
12552             }
12553             for(var i = 0, len = nodeInfo.length; i < len; i++){
12554                 this.select(nodeInfo[i], true, true);
12555             }
12556             return;
12557         } 
12558         var node = this.getNode(nodeInfo);
12559         if(!node || this.isSelected(node)){
12560             return; // already selected.
12561         }
12562         if(!keepExisting){
12563             this.clearSelections(true);
12564         }
12565         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12566             Roo.fly(node).addClass(this.selectedClass);
12567             this.selections.push(node);
12568             if(!suppressEvent){
12569                 this.fireEvent("selectionchange", this, this.selections);
12570             }
12571         }
12572         
12573         
12574     },
12575       /**
12576      * Unselects nodes.
12577      * @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
12578      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12579      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12580      */
12581     unselect : function(nodeInfo, keepExisting, suppressEvent)
12582     {
12583         if(nodeInfo instanceof Array){
12584             Roo.each(this.selections, function(s) {
12585                 this.unselect(s, nodeInfo);
12586             }, this);
12587             return;
12588         }
12589         var node = this.getNode(nodeInfo);
12590         if(!node || !this.isSelected(node)){
12591             Roo.log("not selected");
12592             return; // not selected.
12593         }
12594         // fireevent???
12595         var ns = [];
12596         Roo.each(this.selections, function(s) {
12597             if (s == node ) {
12598                 Roo.fly(node).removeClass(this.selectedClass);
12599
12600                 return;
12601             }
12602             ns.push(s);
12603         },this);
12604         
12605         this.selections= ns;
12606         this.fireEvent("selectionchange", this, this.selections);
12607     },
12608
12609     /**
12610      * Gets a template node.
12611      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12612      * @return {HTMLElement} The node or null if it wasn't found
12613      */
12614     getNode : function(nodeInfo){
12615         if(typeof nodeInfo == "string"){
12616             return document.getElementById(nodeInfo);
12617         }else if(typeof nodeInfo == "number"){
12618             return this.nodes[nodeInfo];
12619         }
12620         return nodeInfo;
12621     },
12622
12623     /**
12624      * Gets a range template nodes.
12625      * @param {Number} startIndex
12626      * @param {Number} endIndex
12627      * @return {Array} An array of nodes
12628      */
12629     getNodes : function(start, end){
12630         var ns = this.nodes;
12631         start = start || 0;
12632         end = typeof end == "undefined" ? ns.length - 1 : end;
12633         var nodes = [];
12634         if(start <= end){
12635             for(var i = start; i <= end; i++){
12636                 nodes.push(ns[i]);
12637             }
12638         } else{
12639             for(var i = start; i >= end; i--){
12640                 nodes.push(ns[i]);
12641             }
12642         }
12643         return nodes;
12644     },
12645
12646     /**
12647      * Finds the index of the passed node
12648      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12649      * @return {Number} The index of the node or -1
12650      */
12651     indexOf : function(node){
12652         node = this.getNode(node);
12653         if(typeof node.nodeIndex == "number"){
12654             return node.nodeIndex;
12655         }
12656         var ns = this.nodes;
12657         for(var i = 0, len = ns.length; i < len; i++){
12658             if(ns[i] == node){
12659                 return i;
12660             }
12661         }
12662         return -1;
12663     }
12664 });
12665 /*
12666  * - LGPL
12667  *
12668  * based on jquery fullcalendar
12669  * 
12670  */
12671
12672 Roo.bootstrap = Roo.bootstrap || {};
12673 /**
12674  * @class Roo.bootstrap.Calendar
12675  * @extends Roo.bootstrap.Component
12676  * Bootstrap Calendar class
12677  * @cfg {Boolean} loadMask (true|false) default false
12678  * @cfg {Object} header generate the user specific header of the calendar, default false
12679
12680  * @constructor
12681  * Create a new Container
12682  * @param {Object} config The config object
12683  */
12684
12685
12686
12687 Roo.bootstrap.Calendar = function(config){
12688     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12689      this.addEvents({
12690         /**
12691              * @event select
12692              * Fires when a date is selected
12693              * @param {DatePicker} this
12694              * @param {Date} date The selected date
12695              */
12696         'select': true,
12697         /**
12698              * @event monthchange
12699              * Fires when the displayed month changes 
12700              * @param {DatePicker} this
12701              * @param {Date} date The selected month
12702              */
12703         'monthchange': true,
12704         /**
12705              * @event evententer
12706              * Fires when mouse over an event
12707              * @param {Calendar} this
12708              * @param {event} Event
12709              */
12710         'evententer': true,
12711         /**
12712              * @event eventleave
12713              * Fires when the mouse leaves an
12714              * @param {Calendar} this
12715              * @param {event}
12716              */
12717         'eventleave': true,
12718         /**
12719              * @event eventclick
12720              * Fires when the mouse click an
12721              * @param {Calendar} this
12722              * @param {event}
12723              */
12724         'eventclick': true
12725         
12726     });
12727
12728 };
12729
12730 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12731     
12732      /**
12733      * @cfg {Number} startDay
12734      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12735      */
12736     startDay : 0,
12737     
12738     loadMask : false,
12739     
12740     header : false,
12741       
12742     getAutoCreate : function(){
12743         
12744         
12745         var fc_button = function(name, corner, style, content ) {
12746             return Roo.apply({},{
12747                 tag : 'span',
12748                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12749                          (corner.length ?
12750                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12751                             ''
12752                         ),
12753                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12754                 unselectable: 'on'
12755             });
12756         };
12757         
12758         var header = {};
12759         
12760         if(!this.header){
12761             header = {
12762                 tag : 'table',
12763                 cls : 'fc-header',
12764                 style : 'width:100%',
12765                 cn : [
12766                     {
12767                         tag: 'tr',
12768                         cn : [
12769                             {
12770                                 tag : 'td',
12771                                 cls : 'fc-header-left',
12772                                 cn : [
12773                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12774                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12775                                     { tag: 'span', cls: 'fc-header-space' },
12776                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12777
12778
12779                                 ]
12780                             },
12781
12782                             {
12783                                 tag : 'td',
12784                                 cls : 'fc-header-center',
12785                                 cn : [
12786                                     {
12787                                         tag: 'span',
12788                                         cls: 'fc-header-title',
12789                                         cn : {
12790                                             tag: 'H2',
12791                                             html : 'month / year'
12792                                         }
12793                                     }
12794
12795                                 ]
12796                             },
12797                             {
12798                                 tag : 'td',
12799                                 cls : 'fc-header-right',
12800                                 cn : [
12801                               /*      fc_button('month', 'left', '', 'month' ),
12802                                     fc_button('week', '', '', 'week' ),
12803                                     fc_button('day', 'right', '', 'day' )
12804                                 */    
12805
12806                                 ]
12807                             }
12808
12809                         ]
12810                     }
12811                 ]
12812             };
12813         }
12814         
12815         header = this.header;
12816         
12817        
12818         var cal_heads = function() {
12819             var ret = [];
12820             // fixme - handle this.
12821             
12822             for (var i =0; i < Date.dayNames.length; i++) {
12823                 var d = Date.dayNames[i];
12824                 ret.push({
12825                     tag: 'th',
12826                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12827                     html : d.substring(0,3)
12828                 });
12829                 
12830             }
12831             ret[0].cls += ' fc-first';
12832             ret[6].cls += ' fc-last';
12833             return ret;
12834         };
12835         var cal_cell = function(n) {
12836             return  {
12837                 tag: 'td',
12838                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12839                 cn : [
12840                     {
12841                         cn : [
12842                             {
12843                                 cls: 'fc-day-number',
12844                                 html: 'D'
12845                             },
12846                             {
12847                                 cls: 'fc-day-content',
12848                              
12849                                 cn : [
12850                                      {
12851                                         style: 'position: relative;' // height: 17px;
12852                                     }
12853                                 ]
12854                             }
12855                             
12856                             
12857                         ]
12858                     }
12859                 ]
12860                 
12861             }
12862         };
12863         var cal_rows = function() {
12864             
12865             var ret = []
12866             for (var r = 0; r < 6; r++) {
12867                 var row= {
12868                     tag : 'tr',
12869                     cls : 'fc-week',
12870                     cn : []
12871                 };
12872                 
12873                 for (var i =0; i < Date.dayNames.length; i++) {
12874                     var d = Date.dayNames[i];
12875                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12876
12877                 }
12878                 row.cn[0].cls+=' fc-first';
12879                 row.cn[0].cn[0].style = 'min-height:90px';
12880                 row.cn[6].cls+=' fc-last';
12881                 ret.push(row);
12882                 
12883             }
12884             ret[0].cls += ' fc-first';
12885             ret[4].cls += ' fc-prev-last';
12886             ret[5].cls += ' fc-last';
12887             return ret;
12888             
12889         };
12890         
12891         var cal_table = {
12892             tag: 'table',
12893             cls: 'fc-border-separate',
12894             style : 'width:100%',
12895             cellspacing  : 0,
12896             cn : [
12897                 { 
12898                     tag: 'thead',
12899                     cn : [
12900                         { 
12901                             tag: 'tr',
12902                             cls : 'fc-first fc-last',
12903                             cn : cal_heads()
12904                         }
12905                     ]
12906                 },
12907                 { 
12908                     tag: 'tbody',
12909                     cn : cal_rows()
12910                 }
12911                   
12912             ]
12913         };
12914          
12915          var cfg = {
12916             cls : 'fc fc-ltr',
12917             cn : [
12918                 header,
12919                 {
12920                     cls : 'fc-content',
12921                     style : "position: relative;",
12922                     cn : [
12923                         {
12924                             cls : 'fc-view fc-view-month fc-grid',
12925                             style : 'position: relative',
12926                             unselectable : 'on',
12927                             cn : [
12928                                 {
12929                                     cls : 'fc-event-container',
12930                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12931                                 },
12932                                 cal_table
12933                             ]
12934                         }
12935                     ]
12936     
12937                 }
12938            ] 
12939             
12940         };
12941         
12942          
12943         
12944         return cfg;
12945     },
12946     
12947     
12948     initEvents : function()
12949     {
12950         if(!this.store){
12951             throw "can not find store for calendar";
12952         }
12953         
12954         var mark = {
12955             tag: "div",
12956             cls:"x-dlg-mask",
12957             style: "text-align:center",
12958             cn: [
12959                 {
12960                     tag: "div",
12961                     style: "background-color:white;width:50%;margin:250 auto",
12962                     cn: [
12963                         {
12964                             tag: "img",
12965                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12966                         },
12967                         {
12968                             tag: "span",
12969                             html: "Loading"
12970                         }
12971                         
12972                     ]
12973                 }
12974             ]
12975         }
12976         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12977         
12978         var size = this.el.select('.fc-content', true).first().getSize();
12979         this.maskEl.setSize(size.width, size.height);
12980         this.maskEl.enableDisplayMode("block");
12981         if(!this.loadMask){
12982             this.maskEl.hide();
12983         }
12984         
12985         this.store = Roo.factory(this.store, Roo.data);
12986         this.store.on('load', this.onLoad, this);
12987         this.store.on('beforeload', this.onBeforeLoad, this);
12988         
12989         this.resize();
12990         
12991         this.cells = this.el.select('.fc-day',true);
12992         //Roo.log(this.cells);
12993         this.textNodes = this.el.query('.fc-day-number');
12994         this.cells.addClassOnOver('fc-state-hover');
12995         
12996         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12997         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12998         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12999         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13000         
13001         this.on('monthchange', this.onMonthChange, this);
13002         
13003         this.update(new Date().clearTime());
13004     },
13005     
13006     resize : function() {
13007         var sz  = this.el.getSize();
13008         
13009         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13010         this.el.select('.fc-day-content div',true).setHeight(34);
13011     },
13012     
13013     
13014     // private
13015     showPrevMonth : function(e){
13016         this.update(this.activeDate.add("mo", -1));
13017     },
13018     showToday : function(e){
13019         this.update(new Date().clearTime());
13020     },
13021     // private
13022     showNextMonth : function(e){
13023         this.update(this.activeDate.add("mo", 1));
13024     },
13025
13026     // private
13027     showPrevYear : function(){
13028         this.update(this.activeDate.add("y", -1));
13029     },
13030
13031     // private
13032     showNextYear : function(){
13033         this.update(this.activeDate.add("y", 1));
13034     },
13035
13036     
13037    // private
13038     update : function(date)
13039     {
13040         var vd = this.activeDate;
13041         this.activeDate = date;
13042 //        if(vd && this.el){
13043 //            var t = date.getTime();
13044 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13045 //                Roo.log('using add remove');
13046 //                
13047 //                this.fireEvent('monthchange', this, date);
13048 //                
13049 //                this.cells.removeClass("fc-state-highlight");
13050 //                this.cells.each(function(c){
13051 //                   if(c.dateValue == t){
13052 //                       c.addClass("fc-state-highlight");
13053 //                       setTimeout(function(){
13054 //                            try{c.dom.firstChild.focus();}catch(e){}
13055 //                       }, 50);
13056 //                       return false;
13057 //                   }
13058 //                   return true;
13059 //                });
13060 //                return;
13061 //            }
13062 //        }
13063         
13064         var days = date.getDaysInMonth();
13065         
13066         var firstOfMonth = date.getFirstDateOfMonth();
13067         var startingPos = firstOfMonth.getDay()-this.startDay;
13068         
13069         if(startingPos < this.startDay){
13070             startingPos += 7;
13071         }
13072         
13073         var pm = date.add(Date.MONTH, -1);
13074         var prevStart = pm.getDaysInMonth()-startingPos;
13075 //        
13076         this.cells = this.el.select('.fc-day',true);
13077         this.textNodes = this.el.query('.fc-day-number');
13078         this.cells.addClassOnOver('fc-state-hover');
13079         
13080         var cells = this.cells.elements;
13081         var textEls = this.textNodes;
13082         
13083         Roo.each(cells, function(cell){
13084             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13085         });
13086         
13087         days += startingPos;
13088
13089         // convert everything to numbers so it's fast
13090         var day = 86400000;
13091         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13092         //Roo.log(d);
13093         //Roo.log(pm);
13094         //Roo.log(prevStart);
13095         
13096         var today = new Date().clearTime().getTime();
13097         var sel = date.clearTime().getTime();
13098         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13099         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13100         var ddMatch = this.disabledDatesRE;
13101         var ddText = this.disabledDatesText;
13102         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13103         var ddaysText = this.disabledDaysText;
13104         var format = this.format;
13105         
13106         var setCellClass = function(cal, cell){
13107             cell.row = 0;
13108             cell.events = [];
13109             cell.more = [];
13110             //Roo.log('set Cell Class');
13111             cell.title = "";
13112             var t = d.getTime();
13113             
13114             //Roo.log(d);
13115             
13116             cell.dateValue = t;
13117             if(t == today){
13118                 cell.className += " fc-today";
13119                 cell.className += " fc-state-highlight";
13120                 cell.title = cal.todayText;
13121             }
13122             if(t == sel){
13123                 // disable highlight in other month..
13124                 //cell.className += " fc-state-highlight";
13125                 
13126             }
13127             // disabling
13128             if(t < min) {
13129                 cell.className = " fc-state-disabled";
13130                 cell.title = cal.minText;
13131                 return;
13132             }
13133             if(t > max) {
13134                 cell.className = " fc-state-disabled";
13135                 cell.title = cal.maxText;
13136                 return;
13137             }
13138             if(ddays){
13139                 if(ddays.indexOf(d.getDay()) != -1){
13140                     cell.title = ddaysText;
13141                     cell.className = " fc-state-disabled";
13142                 }
13143             }
13144             if(ddMatch && format){
13145                 var fvalue = d.dateFormat(format);
13146                 if(ddMatch.test(fvalue)){
13147                     cell.title = ddText.replace("%0", fvalue);
13148                     cell.className = " fc-state-disabled";
13149                 }
13150             }
13151             
13152             if (!cell.initialClassName) {
13153                 cell.initialClassName = cell.dom.className;
13154             }
13155             
13156             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13157         };
13158
13159         var i = 0;
13160         
13161         for(; i < startingPos; i++) {
13162             textEls[i].innerHTML = (++prevStart);
13163             d.setDate(d.getDate()+1);
13164             
13165             cells[i].className = "fc-past fc-other-month";
13166             setCellClass(this, cells[i]);
13167         }
13168         
13169         var intDay = 0;
13170         
13171         for(; i < days; i++){
13172             intDay = i - startingPos + 1;
13173             textEls[i].innerHTML = (intDay);
13174             d.setDate(d.getDate()+1);
13175             
13176             cells[i].className = ''; // "x-date-active";
13177             setCellClass(this, cells[i]);
13178         }
13179         var extraDays = 0;
13180         
13181         for(; i < 42; i++) {
13182             textEls[i].innerHTML = (++extraDays);
13183             d.setDate(d.getDate()+1);
13184             
13185             cells[i].className = "fc-future fc-other-month";
13186             setCellClass(this, cells[i]);
13187         }
13188         
13189         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13190         
13191         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13192         
13193         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13194         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13195         
13196         if(totalRows != 6){
13197             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13198             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13199         }
13200         
13201         this.fireEvent('monthchange', this, date);
13202         
13203         
13204         /*
13205         if(!this.internalRender){
13206             var main = this.el.dom.firstChild;
13207             var w = main.offsetWidth;
13208             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13209             Roo.fly(main).setWidth(w);
13210             this.internalRender = true;
13211             // opera does not respect the auto grow header center column
13212             // then, after it gets a width opera refuses to recalculate
13213             // without a second pass
13214             if(Roo.isOpera && !this.secondPass){
13215                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13216                 this.secondPass = true;
13217                 this.update.defer(10, this, [date]);
13218             }
13219         }
13220         */
13221         
13222     },
13223     
13224     findCell : function(dt) {
13225         dt = dt.clearTime().getTime();
13226         var ret = false;
13227         this.cells.each(function(c){
13228             //Roo.log("check " +c.dateValue + '?=' + dt);
13229             if(c.dateValue == dt){
13230                 ret = c;
13231                 return false;
13232             }
13233             return true;
13234         });
13235         
13236         return ret;
13237     },
13238     
13239     findCells : function(ev) {
13240         var s = ev.start.clone().clearTime().getTime();
13241        // Roo.log(s);
13242         var e= ev.end.clone().clearTime().getTime();
13243        // Roo.log(e);
13244         var ret = [];
13245         this.cells.each(function(c){
13246              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13247             
13248             if(c.dateValue > e){
13249                 return ;
13250             }
13251             if(c.dateValue < s){
13252                 return ;
13253             }
13254             ret.push(c);
13255         });
13256         
13257         return ret;    
13258     },
13259     
13260 //    findBestRow: function(cells)
13261 //    {
13262 //        var ret = 0;
13263 //        
13264 //        for (var i =0 ; i < cells.length;i++) {
13265 //            ret  = Math.max(cells[i].rows || 0,ret);
13266 //        }
13267 //        return ret;
13268 //        
13269 //    },
13270     
13271     
13272     addItem : function(ev)
13273     {
13274         // look for vertical location slot in
13275         var cells = this.findCells(ev);
13276         
13277 //        ev.row = this.findBestRow(cells);
13278         
13279         // work out the location.
13280         
13281         var crow = false;
13282         var rows = [];
13283         for(var i =0; i < cells.length; i++) {
13284             
13285             cells[i].row = cells[0].row;
13286             
13287             if(i == 0){
13288                 cells[i].row = cells[i].row + 1;
13289             }
13290             
13291             if (!crow) {
13292                 crow = {
13293                     start : cells[i],
13294                     end :  cells[i]
13295                 };
13296                 continue;
13297             }
13298             if (crow.start.getY() == cells[i].getY()) {
13299                 // on same row.
13300                 crow.end = cells[i];
13301                 continue;
13302             }
13303             // different row.
13304             rows.push(crow);
13305             crow = {
13306                 start: cells[i],
13307                 end : cells[i]
13308             };
13309             
13310         }
13311         
13312         rows.push(crow);
13313         ev.els = [];
13314         ev.rows = rows;
13315         ev.cells = cells;
13316         
13317         cells[0].events.push(ev);
13318         
13319         this.calevents.push(ev);
13320     },
13321     
13322     clearEvents: function() {
13323         
13324         if(!this.calevents){
13325             return;
13326         }
13327         
13328         Roo.each(this.cells.elements, function(c){
13329             c.row = 0;
13330             c.events = [];
13331             c.more = [];
13332         });
13333         
13334         Roo.each(this.calevents, function(e) {
13335             Roo.each(e.els, function(el) {
13336                 el.un('mouseenter' ,this.onEventEnter, this);
13337                 el.un('mouseleave' ,this.onEventLeave, this);
13338                 el.remove();
13339             },this);
13340         },this);
13341         
13342         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13343             e.remove();
13344         });
13345         
13346     },
13347     
13348     renderEvents: function()
13349     {   
13350         var _this = this;
13351         
13352         this.cells.each(function(c) {
13353             
13354             if(c.row < 5){
13355                 return;
13356             }
13357             
13358             var ev = c.events;
13359             
13360             var r = 4;
13361             if(c.row != c.events.length){
13362                 r = 4 - (4 - (c.row - c.events.length));
13363             }
13364             
13365             c.events = ev.slice(0, r);
13366             c.more = ev.slice(r);
13367             
13368             if(c.more.length && c.more.length == 1){
13369                 c.events.push(c.more.pop());
13370             }
13371             
13372             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13373             
13374         });
13375             
13376         this.cells.each(function(c) {
13377             
13378             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13379             
13380             
13381             for (var e = 0; e < c.events.length; e++){
13382                 var ev = c.events[e];
13383                 var rows = ev.rows;
13384                 
13385                 for(var i = 0; i < rows.length; i++) {
13386                 
13387                     // how many rows should it span..
13388
13389                     var  cfg = {
13390                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13391                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13392
13393                         unselectable : "on",
13394                         cn : [
13395                             {
13396                                 cls: 'fc-event-inner',
13397                                 cn : [
13398     //                                {
13399     //                                  tag:'span',
13400     //                                  cls: 'fc-event-time',
13401     //                                  html : cells.length > 1 ? '' : ev.time
13402     //                                },
13403                                     {
13404                                       tag:'span',
13405                                       cls: 'fc-event-title',
13406                                       html : String.format('{0}', ev.title)
13407                                     }
13408
13409
13410                                 ]
13411                             },
13412                             {
13413                                 cls: 'ui-resizable-handle ui-resizable-e',
13414                                 html : '&nbsp;&nbsp;&nbsp'
13415                             }
13416
13417                         ]
13418                     };
13419
13420                     if (i == 0) {
13421                         cfg.cls += ' fc-event-start';
13422                     }
13423                     if ((i+1) == rows.length) {
13424                         cfg.cls += ' fc-event-end';
13425                     }
13426
13427                     var ctr = _this.el.select('.fc-event-container',true).first();
13428                     var cg = ctr.createChild(cfg);
13429
13430                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13431                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13432
13433                     var r = (c.more.length) ? 1 : 0;
13434                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13435                     cg.setWidth(ebox.right - sbox.x -2);
13436
13437                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13438                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13439                     cg.on('click', _this.onEventClick, _this, ev);
13440
13441                     ev.els.push(cg);
13442                     
13443                 }
13444                 
13445             }
13446             
13447             
13448             if(c.more.length){
13449                 var  cfg = {
13450                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13451                     style : 'position: absolute',
13452                     unselectable : "on",
13453                     cn : [
13454                         {
13455                             cls: 'fc-event-inner',
13456                             cn : [
13457                                 {
13458                                   tag:'span',
13459                                   cls: 'fc-event-title',
13460                                   html : 'More'
13461                                 }
13462
13463
13464                             ]
13465                         },
13466                         {
13467                             cls: 'ui-resizable-handle ui-resizable-e',
13468                             html : '&nbsp;&nbsp;&nbsp'
13469                         }
13470
13471                     ]
13472                 };
13473
13474                 var ctr = _this.el.select('.fc-event-container',true).first();
13475                 var cg = ctr.createChild(cfg);
13476
13477                 var sbox = c.select('.fc-day-content',true).first().getBox();
13478                 var ebox = c.select('.fc-day-content',true).first().getBox();
13479                 //Roo.log(cg);
13480                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13481                 cg.setWidth(ebox.right - sbox.x -2);
13482
13483                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13484                 
13485             }
13486             
13487         });
13488         
13489         
13490         
13491     },
13492     
13493     onEventEnter: function (e, el,event,d) {
13494         this.fireEvent('evententer', this, el, event);
13495     },
13496     
13497     onEventLeave: function (e, el,event,d) {
13498         this.fireEvent('eventleave', this, el, event);
13499     },
13500     
13501     onEventClick: function (e, el,event,d) {
13502         this.fireEvent('eventclick', this, el, event);
13503     },
13504     
13505     onMonthChange: function () {
13506         this.store.load();
13507     },
13508     
13509     onMoreEventClick: function(e, el, more)
13510     {
13511         var _this = this;
13512         
13513         this.calpopover.placement = 'right';
13514         this.calpopover.setTitle('More');
13515         
13516         this.calpopover.setContent('');
13517         
13518         var ctr = this.calpopover.el.select('.popover-content', true).first();
13519         
13520         Roo.each(more, function(m){
13521             var cfg = {
13522                 cls : 'fc-event-hori fc-event-draggable',
13523                 html : m.title
13524             }
13525             var cg = ctr.createChild(cfg);
13526             
13527             cg.on('click', _this.onEventClick, _this, m);
13528         });
13529         
13530         this.calpopover.show(el);
13531         
13532         
13533     },
13534     
13535     onLoad: function () 
13536     {   
13537         this.calevents = [];
13538         var cal = this;
13539         
13540         if(this.store.getCount() > 0){
13541             this.store.data.each(function(d){
13542                cal.addItem({
13543                     id : d.data.id,
13544                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13545                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13546                     time : d.data.start_time,
13547                     title : d.data.title,
13548                     description : d.data.description,
13549                     venue : d.data.venue
13550                 });
13551             });
13552         }
13553         
13554         this.renderEvents();
13555         
13556         if(this.calevents.length && this.loadMask){
13557             this.maskEl.hide();
13558         }
13559     },
13560     
13561     onBeforeLoad: function()
13562     {
13563         this.clearEvents();
13564         if(this.loadMask){
13565             this.maskEl.show();
13566         }
13567     }
13568 });
13569
13570  
13571  /*
13572  * - LGPL
13573  *
13574  * element
13575  * 
13576  */
13577
13578 /**
13579  * @class Roo.bootstrap.Popover
13580  * @extends Roo.bootstrap.Component
13581  * Bootstrap Popover class
13582  * @cfg {String} html contents of the popover   (or false to use children..)
13583  * @cfg {String} title of popover (or false to hide)
13584  * @cfg {String} placement how it is placed
13585  * @cfg {String} trigger click || hover (or false to trigger manually)
13586  * @cfg {String} over what (parent or false to trigger manually.)
13587  * 
13588  * @constructor
13589  * Create a new Popover
13590  * @param {Object} config The config object
13591  */
13592
13593 Roo.bootstrap.Popover = function(config){
13594     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13595 };
13596
13597 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13598     
13599     title: 'Fill in a title',
13600     html: false,
13601     
13602     placement : 'right',
13603     trigger : 'hover', // hover
13604     
13605     over: 'parent',
13606     
13607     can_build_overlaid : false,
13608     
13609     getChildContainer : function()
13610     {
13611         return this.el.select('.popover-content',true).first();
13612     },
13613     
13614     getAutoCreate : function(){
13615          Roo.log('make popover?');
13616         var cfg = {
13617            cls : 'popover roo-dynamic',
13618            style: 'display:block',
13619            cn : [
13620                 {
13621                     cls : 'arrow'
13622                 },
13623                 {
13624                     cls : 'popover-inner',
13625                     cn : [
13626                         {
13627                             tag: 'h3',
13628                             cls: 'popover-title',
13629                             html : this.title
13630                         },
13631                         {
13632                             cls : 'popover-content',
13633                             html : this.html
13634                         }
13635                     ]
13636                     
13637                 }
13638            ]
13639         };
13640         
13641         return cfg;
13642     },
13643     setTitle: function(str)
13644     {
13645         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13646     },
13647     setContent: function(str)
13648     {
13649         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13650     },
13651     // as it get's added to the bottom of the page.
13652     onRender : function(ct, position)
13653     {
13654         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13655         if(!this.el){
13656             var cfg = Roo.apply({},  this.getAutoCreate());
13657             cfg.id = Roo.id();
13658             
13659             if (this.cls) {
13660                 cfg.cls += ' ' + this.cls;
13661             }
13662             if (this.style) {
13663                 cfg.style = this.style;
13664             }
13665             Roo.log("adding to ")
13666             this.el = Roo.get(document.body).createChild(cfg, position);
13667             Roo.log(this.el);
13668         }
13669         this.initEvents();
13670     },
13671     
13672     initEvents : function()
13673     {
13674         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13675         this.el.enableDisplayMode('block');
13676         this.el.hide();
13677         if (this.over === false) {
13678             return; 
13679         }
13680         if (this.triggers === false) {
13681             return;
13682         }
13683         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13684         var triggers = this.trigger ? this.trigger.split(' ') : [];
13685         Roo.each(triggers, function(trigger) {
13686         
13687             if (trigger == 'click') {
13688                 on_el.on('click', this.toggle, this);
13689             } else if (trigger != 'manual') {
13690                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13691                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13692       
13693                 on_el.on(eventIn  ,this.enter, this);
13694                 on_el.on(eventOut, this.leave, this);
13695             }
13696         }, this);
13697         
13698     },
13699     
13700     
13701     // private
13702     timeout : null,
13703     hoverState : null,
13704     
13705     toggle : function () {
13706         this.hoverState == 'in' ? this.leave() : this.enter();
13707     },
13708     
13709     enter : function () {
13710        
13711     
13712         clearTimeout(this.timeout);
13713     
13714         this.hoverState = 'in'
13715     
13716         if (!this.delay || !this.delay.show) {
13717             this.show();
13718             return 
13719         }
13720         var _t = this;
13721         this.timeout = setTimeout(function () {
13722             if (_t.hoverState == 'in') {
13723                 _t.show();
13724             }
13725         }, this.delay.show)
13726     },
13727     leave : function() {
13728         clearTimeout(this.timeout);
13729     
13730         this.hoverState = 'out'
13731     
13732         if (!this.delay || !this.delay.hide) {
13733             this.hide();
13734             return 
13735         }
13736         var _t = this;
13737         this.timeout = setTimeout(function () {
13738             if (_t.hoverState == 'out') {
13739                 _t.hide();
13740             }
13741         }, this.delay.hide)
13742     },
13743     
13744     show : function (on_el)
13745     {
13746         if (!on_el) {
13747             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13748         }
13749         // set content.
13750         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13751         if (this.html !== false) {
13752             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13753         }
13754         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13755         if (!this.title.length) {
13756             this.el.select('.popover-title',true).hide();
13757         }
13758         
13759         var placement = typeof this.placement == 'function' ?
13760             this.placement.call(this, this.el, on_el) :
13761             this.placement;
13762             
13763         var autoToken = /\s?auto?\s?/i;
13764         var autoPlace = autoToken.test(placement);
13765         if (autoPlace) {
13766             placement = placement.replace(autoToken, '') || 'top';
13767         }
13768         
13769         //this.el.detach()
13770         //this.el.setXY([0,0]);
13771         this.el.show();
13772         this.el.dom.style.display='block';
13773         this.el.addClass(placement);
13774         
13775         //this.el.appendTo(on_el);
13776         
13777         var p = this.getPosition();
13778         var box = this.el.getBox();
13779         
13780         if (autoPlace) {
13781             // fixme..
13782         }
13783         var align = Roo.bootstrap.Popover.alignment[placement]
13784         this.el.alignTo(on_el, align[0],align[1]);
13785         //var arrow = this.el.select('.arrow',true).first();
13786         //arrow.set(align[2], 
13787         
13788         this.el.addClass('in');
13789         this.hoverState = null;
13790         
13791         if (this.el.hasClass('fade')) {
13792             // fade it?
13793         }
13794         
13795     },
13796     hide : function()
13797     {
13798         this.el.setXY([0,0]);
13799         this.el.removeClass('in');
13800         this.el.hide();
13801         
13802     }
13803     
13804 });
13805
13806 Roo.bootstrap.Popover.alignment = {
13807     'left' : ['r-l', [-10,0], 'right'],
13808     'right' : ['l-r', [10,0], 'left'],
13809     'bottom' : ['t-b', [0,10], 'top'],
13810     'top' : [ 'b-t', [0,-10], 'bottom']
13811 };
13812
13813  /*
13814  * - LGPL
13815  *
13816  * Progress
13817  * 
13818  */
13819
13820 /**
13821  * @class Roo.bootstrap.Progress
13822  * @extends Roo.bootstrap.Component
13823  * Bootstrap Progress class
13824  * @cfg {Boolean} striped striped of the progress bar
13825  * @cfg {Boolean} active animated of the progress bar
13826  * 
13827  * 
13828  * @constructor
13829  * Create a new Progress
13830  * @param {Object} config The config object
13831  */
13832
13833 Roo.bootstrap.Progress = function(config){
13834     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13835 };
13836
13837 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13838     
13839     striped : false,
13840     active: false,
13841     
13842     getAutoCreate : function(){
13843         var cfg = {
13844             tag: 'div',
13845             cls: 'progress'
13846         };
13847         
13848         
13849         if(this.striped){
13850             cfg.cls += ' progress-striped';
13851         }
13852       
13853         if(this.active){
13854             cfg.cls += ' active';
13855         }
13856         
13857         
13858         return cfg;
13859     }
13860    
13861 });
13862
13863  
13864
13865  /*
13866  * - LGPL
13867  *
13868  * ProgressBar
13869  * 
13870  */
13871
13872 /**
13873  * @class Roo.bootstrap.ProgressBar
13874  * @extends Roo.bootstrap.Component
13875  * Bootstrap ProgressBar class
13876  * @cfg {Number} aria_valuenow aria-value now
13877  * @cfg {Number} aria_valuemin aria-value min
13878  * @cfg {Number} aria_valuemax aria-value max
13879  * @cfg {String} label label for the progress bar
13880  * @cfg {String} panel (success | info | warning | danger )
13881  * @cfg {String} role role of the progress bar
13882  * @cfg {String} sr_only text
13883  * 
13884  * 
13885  * @constructor
13886  * Create a new ProgressBar
13887  * @param {Object} config The config object
13888  */
13889
13890 Roo.bootstrap.ProgressBar = function(config){
13891     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13892 };
13893
13894 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13895     
13896     aria_valuenow : 0,
13897     aria_valuemin : 0,
13898     aria_valuemax : 100,
13899     label : false,
13900     panel : false,
13901     role : false,
13902     sr_only: false,
13903     
13904     getAutoCreate : function()
13905     {
13906         
13907         var cfg = {
13908             tag: 'div',
13909             cls: 'progress-bar',
13910             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13911         };
13912         
13913         if(this.sr_only){
13914             cfg.cn = {
13915                 tag: 'span',
13916                 cls: 'sr-only',
13917                 html: this.sr_only
13918             }
13919         }
13920         
13921         if(this.role){
13922             cfg.role = this.role;
13923         }
13924         
13925         if(this.aria_valuenow){
13926             cfg['aria-valuenow'] = this.aria_valuenow;
13927         }
13928         
13929         if(this.aria_valuemin){
13930             cfg['aria-valuemin'] = this.aria_valuemin;
13931         }
13932         
13933         if(this.aria_valuemax){
13934             cfg['aria-valuemax'] = this.aria_valuemax;
13935         }
13936         
13937         if(this.label && !this.sr_only){
13938             cfg.html = this.label;
13939         }
13940         
13941         if(this.panel){
13942             cfg.cls += ' progress-bar-' + this.panel;
13943         }
13944         
13945         return cfg;
13946     },
13947     
13948     update : function(aria_valuenow)
13949     {
13950         this.aria_valuenow = aria_valuenow;
13951         
13952         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13953     }
13954    
13955 });
13956
13957  
13958
13959  /*
13960  * - LGPL
13961  *
13962  * column
13963  * 
13964  */
13965
13966 /**
13967  * @class Roo.bootstrap.TabGroup
13968  * @extends Roo.bootstrap.Column
13969  * Bootstrap Column class
13970  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13971  * @cfg {Boolean} carousel true to make the group behave like a carousel
13972  * 
13973  * @constructor
13974  * Create a new TabGroup
13975  * @param {Object} config The config object
13976  */
13977
13978 Roo.bootstrap.TabGroup = function(config){
13979     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13980     if (!this.navId) {
13981         this.navId = Roo.id();
13982     }
13983     this.tabs = [];
13984     Roo.bootstrap.TabGroup.register(this);
13985     
13986 };
13987
13988 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13989     
13990     carousel : false,
13991     transition : false,
13992      
13993     getAutoCreate : function()
13994     {
13995         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13996         
13997         cfg.cls += ' tab-content';
13998         
13999         if (this.carousel) {
14000             cfg.cls += ' carousel slide';
14001             cfg.cn = [{
14002                cls : 'carousel-inner'
14003             }]
14004         }
14005         
14006         
14007         return cfg;
14008     },
14009     getChildContainer : function()
14010     {
14011         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14012     },
14013     
14014     /**
14015     * register a Navigation item
14016     * @param {Roo.bootstrap.NavItem} the navitem to add
14017     */
14018     register : function(item)
14019     {
14020         this.tabs.push( item);
14021         item.navId = this.navId; // not really needed..
14022     
14023     },
14024     
14025     getActivePanel : function()
14026     {
14027         var r = false;
14028         Roo.each(this.tabs, function(t) {
14029             if (t.active) {
14030                 r = t;
14031                 return false;
14032             }
14033             return null;
14034         });
14035         return r;
14036         
14037     },
14038     getPanelByName : function(n)
14039     {
14040         var r = false;
14041         Roo.each(this.tabs, function(t) {
14042             if (t.tabId == n) {
14043                 r = t;
14044                 return false;
14045             }
14046             return null;
14047         });
14048         return r;
14049     },
14050     indexOfPanel : function(p)
14051     {
14052         var r = false;
14053         Roo.each(this.tabs, function(t,i) {
14054             if (t.tabId == p.tabId) {
14055                 r = i;
14056                 return false;
14057             }
14058             return null;
14059         });
14060         return r;
14061     },
14062     /**
14063      * show a specific panel
14064      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14065      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14066      */
14067     showPanel : function (pan)
14068     {
14069         
14070         if (typeof(pan) == 'number') {
14071             pan = this.tabs[pan];
14072         }
14073         if (typeof(pan) == 'string') {
14074             pan = this.getPanelByName(pan);
14075         }
14076         if (pan.tabId == this.getActivePanel().tabId) {
14077             return true;
14078         }
14079         var cur = this.getActivePanel();
14080         
14081         if (false === cur.fireEvent('beforedeactivate')) {
14082             return false;
14083         }
14084         
14085         if (this.carousel) {
14086             this.transition = true;
14087             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14088             var lr = dir == 'next' ? 'left' : 'right';
14089             pan.el.addClass(dir); // or prev
14090             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14091             cur.el.addClass(lr); // or right
14092             pan.el.addClass(lr);
14093             
14094             var _this = this;
14095             cur.el.on('transitionend', function() {
14096                 Roo.log("trans end?");
14097                 
14098                 pan.el.removeClass([lr,dir]);
14099                 pan.setActive(true);
14100                 
14101                 cur.el.removeClass([lr]);
14102                 cur.setActive(false);
14103                 
14104                 _this.transition = false;
14105                 
14106             }, this, { single:  true } );
14107             return true;
14108         }
14109         
14110         cur.setActive(false);
14111         pan.setActive(true);
14112         return true;
14113         
14114     },
14115     showPanelNext : function()
14116     {
14117         var i = this.indexOfPanel(this.getActivePanel());
14118         if (i > this.tabs.length) {
14119             return;
14120         }
14121         this.showPanel(this.tabs[i+1]);
14122     },
14123     showPanelPrev : function()
14124     {
14125         var i = this.indexOfPanel(this.getActivePanel());
14126         if (i  < 1) {
14127             return;
14128         }
14129         this.showPanel(this.tabs[i-1]);
14130     }
14131     
14132     
14133   
14134 });
14135
14136  
14137
14138  
14139  
14140 Roo.apply(Roo.bootstrap.TabGroup, {
14141     
14142     groups: {},
14143      /**
14144     * register a Navigation Group
14145     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14146     */
14147     register : function(navgrp)
14148     {
14149         this.groups[navgrp.navId] = navgrp;
14150         
14151     },
14152     /**
14153     * fetch a Navigation Group based on the navigation ID
14154     * if one does not exist , it will get created.
14155     * @param {string} the navgroup to add
14156     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14157     */
14158     get: function(navId) {
14159         if (typeof(this.groups[navId]) == 'undefined') {
14160             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14161         }
14162         return this.groups[navId] ;
14163     }
14164     
14165     
14166     
14167 });
14168
14169  /*
14170  * - LGPL
14171  *
14172  * TabPanel
14173  * 
14174  */
14175
14176 /**
14177  * @class Roo.bootstrap.TabPanel
14178  * @extends Roo.bootstrap.Component
14179  * Bootstrap TabPanel class
14180  * @cfg {Boolean} active panel active
14181  * @cfg {String} html panel content
14182  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14183  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14184  * 
14185  * 
14186  * @constructor
14187  * Create a new TabPanel
14188  * @param {Object} config The config object
14189  */
14190
14191 Roo.bootstrap.TabPanel = function(config){
14192     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14193     this.addEvents({
14194         /**
14195              * @event changed
14196              * Fires when the active status changes
14197              * @param {Roo.bootstrap.TabPanel} this
14198              * @param {Boolean} state the new state
14199             
14200          */
14201         'changed': true,
14202         /**
14203              * @event beforedeactivate
14204              * Fires before a tab is de-activated - can be used to do validation on a form.
14205              * @param {Roo.bootstrap.TabPanel} this
14206              * @return {Boolean} false if there is an error
14207             
14208          */
14209         'beforedeactivate': true
14210      });
14211     
14212     this.tabId = this.tabId || Roo.id();
14213   
14214 };
14215
14216 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14217     
14218     active: false,
14219     html: false,
14220     tabId: false,
14221     navId : false,
14222     
14223     getAutoCreate : function(){
14224         var cfg = {
14225             tag: 'div',
14226             // item is needed for carousel - not sure if it has any effect otherwise
14227             cls: 'tab-pane item',
14228             html: this.html || ''
14229         };
14230         
14231         if(this.active){
14232             cfg.cls += ' active';
14233         }
14234         
14235         if(this.tabId){
14236             cfg.tabId = this.tabId;
14237         }
14238         
14239         
14240         return cfg;
14241     },
14242     
14243     initEvents:  function()
14244     {
14245         Roo.log('-------- init events on tab panel ---------');
14246         
14247         var p = this.parent();
14248         this.navId = this.navId || p.navId;
14249         
14250         if (typeof(this.navId) != 'undefined') {
14251             // not really needed.. but just in case.. parent should be a NavGroup.
14252             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14253             Roo.log(['register', tg, this]);
14254             tg.register(this);
14255         }
14256     },
14257     
14258     
14259     onRender : function(ct, position)
14260     {
14261        // Roo.log("Call onRender: " + this.xtype);
14262         
14263         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14264         
14265         
14266         
14267         
14268         
14269     },
14270     
14271     setActive: function(state)
14272     {
14273         Roo.log("panel - set active " + this.tabId + "=" + state);
14274         
14275         this.active = state;
14276         if (!state) {
14277             this.el.removeClass('active');
14278             
14279         } else  if (!this.el.hasClass('active')) {
14280             this.el.addClass('active');
14281         }
14282         this.fireEvent('changed', this, state);
14283     }
14284     
14285     
14286 });
14287  
14288
14289  
14290
14291  /*
14292  * - LGPL
14293  *
14294  * DateField
14295  * 
14296  */
14297
14298 /**
14299  * @class Roo.bootstrap.DateField
14300  * @extends Roo.bootstrap.Input
14301  * Bootstrap DateField class
14302  * @cfg {Number} weekStart default 0
14303  * @cfg {Number} weekStart default 0
14304  * @cfg {Number} viewMode default empty, (months|years)
14305  * @cfg {Number} minViewMode default empty, (months|years)
14306  * @cfg {Number} startDate default -Infinity
14307  * @cfg {Number} endDate default Infinity
14308  * @cfg {Boolean} todayHighlight default false
14309  * @cfg {Boolean} todayBtn default false
14310  * @cfg {Boolean} calendarWeeks default false
14311  * @cfg {Object} daysOfWeekDisabled default empty
14312  * 
14313  * @cfg {Boolean} keyboardNavigation default true
14314  * @cfg {String} language default en
14315  * 
14316  * @constructor
14317  * Create a new DateField
14318  * @param {Object} config The config object
14319  */
14320
14321 Roo.bootstrap.DateField = function(config){
14322     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14323      this.addEvents({
14324             /**
14325              * @event show
14326              * Fires when this field show.
14327              * @param {Roo.bootstrap.DateField} this
14328              * @param {Mixed} date The date value
14329              */
14330             show : true,
14331             /**
14332              * @event show
14333              * Fires when this field hide.
14334              * @param {Roo.bootstrap.DateField} this
14335              * @param {Mixed} date The date value
14336              */
14337             hide : true,
14338             /**
14339              * @event select
14340              * Fires when select a date.
14341              * @param {Roo.bootstrap.DateField} this
14342              * @param {Mixed} date The date value
14343              */
14344             select : true
14345         });
14346 };
14347
14348 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14349     
14350     /**
14351      * @cfg {String} format
14352      * The default date format string which can be overriden for localization support.  The format must be
14353      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14354      */
14355     format : "m/d/y",
14356     /**
14357      * @cfg {String} altFormats
14358      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14359      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14360      */
14361     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14362     
14363     weekStart : 0,
14364     
14365     viewMode : '',
14366     
14367     minViewMode : '',
14368     
14369     todayHighlight : false,
14370     
14371     todayBtn: false,
14372     
14373     language: 'en',
14374     
14375     keyboardNavigation: true,
14376     
14377     calendarWeeks: false,
14378     
14379     startDate: -Infinity,
14380     
14381     endDate: Infinity,
14382     
14383     daysOfWeekDisabled: [],
14384     
14385     _events: [],
14386     
14387     UTCDate: function()
14388     {
14389         return new Date(Date.UTC.apply(Date, arguments));
14390     },
14391     
14392     UTCToday: function()
14393     {
14394         var today = new Date();
14395         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14396     },
14397     
14398     getDate: function() {
14399             var d = this.getUTCDate();
14400             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14401     },
14402     
14403     getUTCDate: function() {
14404             return this.date;
14405     },
14406     
14407     setDate: function(d) {
14408             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14409     },
14410     
14411     setUTCDate: function(d) {
14412             this.date = d;
14413             this.setValue(this.formatDate(this.date));
14414     },
14415         
14416     onRender: function(ct, position)
14417     {
14418         
14419         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14420         
14421         this.language = this.language || 'en';
14422         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14423         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14424         
14425         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14426         this.format = this.format || 'm/d/y';
14427         this.isInline = false;
14428         this.isInput = true;
14429         this.component = this.el.select('.add-on', true).first() || false;
14430         this.component = (this.component && this.component.length === 0) ? false : this.component;
14431         this.hasInput = this.component && this.inputEL().length;
14432         
14433         if (typeof(this.minViewMode === 'string')) {
14434             switch (this.minViewMode) {
14435                 case 'months':
14436                     this.minViewMode = 1;
14437                     break;
14438                 case 'years':
14439                     this.minViewMode = 2;
14440                     break;
14441                 default:
14442                     this.minViewMode = 0;
14443                     break;
14444             }
14445         }
14446         
14447         if (typeof(this.viewMode === 'string')) {
14448             switch (this.viewMode) {
14449                 case 'months':
14450                     this.viewMode = 1;
14451                     break;
14452                 case 'years':
14453                     this.viewMode = 2;
14454                     break;
14455                 default:
14456                     this.viewMode = 0;
14457                     break;
14458             }
14459         }
14460                 
14461         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14462         
14463 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14464         
14465         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14466         
14467         this.picker().on('mousedown', this.onMousedown, this);
14468         this.picker().on('click', this.onClick, this);
14469         
14470         this.picker().addClass('datepicker-dropdown');
14471         
14472         this.startViewMode = this.viewMode;
14473         
14474         
14475         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14476             if(!this.calendarWeeks){
14477                 v.remove();
14478                 return;
14479             };
14480             
14481             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14482             v.attr('colspan', function(i, val){
14483                 return parseInt(val) + 1;
14484             });
14485         })
14486                         
14487         
14488         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14489         
14490         this.setStartDate(this.startDate);
14491         this.setEndDate(this.endDate);
14492         
14493         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14494         
14495         this.fillDow();
14496         this.fillMonths();
14497         this.update();
14498         this.showMode();
14499         
14500         if(this.isInline) {
14501             this.show();
14502         }
14503     },
14504     
14505     picker : function()
14506     {
14507         return this.pickerEl;
14508 //        return this.el.select('.datepicker', true).first();
14509     },
14510     
14511     fillDow: function()
14512     {
14513         var dowCnt = this.weekStart;
14514         
14515         var dow = {
14516             tag: 'tr',
14517             cn: [
14518                 
14519             ]
14520         };
14521         
14522         if(this.calendarWeeks){
14523             dow.cn.push({
14524                 tag: 'th',
14525                 cls: 'cw',
14526                 html: '&nbsp;'
14527             })
14528         }
14529         
14530         while (dowCnt < this.weekStart + 7) {
14531             dow.cn.push({
14532                 tag: 'th',
14533                 cls: 'dow',
14534                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14535             });
14536         }
14537         
14538         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14539     },
14540     
14541     fillMonths: function()
14542     {    
14543         var i = 0
14544         var months = this.picker().select('>.datepicker-months td', true).first();
14545         
14546         months.dom.innerHTML = '';
14547         
14548         while (i < 12) {
14549             var month = {
14550                 tag: 'span',
14551                 cls: 'month',
14552                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14553             }
14554             
14555             months.createChild(month);
14556         }
14557         
14558     },
14559     
14560     update: function()
14561     {
14562         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14563         
14564         if (this.date < this.startDate) {
14565             this.viewDate = new Date(this.startDate);
14566         } else if (this.date > this.endDate) {
14567             this.viewDate = new Date(this.endDate);
14568         } else {
14569             this.viewDate = new Date(this.date);
14570         }
14571         
14572         this.fill();
14573     },
14574     
14575     fill: function() 
14576     {
14577         var d = new Date(this.viewDate),
14578                 year = d.getUTCFullYear(),
14579                 month = d.getUTCMonth(),
14580                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14581                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14582                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14583                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14584                 currentDate = this.date && this.date.valueOf(),
14585                 today = this.UTCToday();
14586         
14587         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14588         
14589 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14590         
14591 //        this.picker.select('>tfoot th.today').
14592 //                                              .text(dates[this.language].today)
14593 //                                              .toggle(this.todayBtn !== false);
14594     
14595         this.updateNavArrows();
14596         this.fillMonths();
14597                                                 
14598         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14599         
14600         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14601          
14602         prevMonth.setUTCDate(day);
14603         
14604         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14605         
14606         var nextMonth = new Date(prevMonth);
14607         
14608         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14609         
14610         nextMonth = nextMonth.valueOf();
14611         
14612         var fillMonths = false;
14613         
14614         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14615         
14616         while(prevMonth.valueOf() < nextMonth) {
14617             var clsName = '';
14618             
14619             if (prevMonth.getUTCDay() === this.weekStart) {
14620                 if(fillMonths){
14621                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14622                 }
14623                     
14624                 fillMonths = {
14625                     tag: 'tr',
14626                     cn: []
14627                 };
14628                 
14629                 if(this.calendarWeeks){
14630                     // ISO 8601: First week contains first thursday.
14631                     // ISO also states week starts on Monday, but we can be more abstract here.
14632                     var
14633                     // Start of current week: based on weekstart/current date
14634                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14635                     // Thursday of this week
14636                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14637                     // First Thursday of year, year from thursday
14638                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14639                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14640                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14641                     
14642                     fillMonths.cn.push({
14643                         tag: 'td',
14644                         cls: 'cw',
14645                         html: calWeek
14646                     });
14647                 }
14648             }
14649             
14650             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14651                 clsName += ' old';
14652             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14653                 clsName += ' new';
14654             }
14655             if (this.todayHighlight &&
14656                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14657                 prevMonth.getUTCMonth() == today.getMonth() &&
14658                 prevMonth.getUTCDate() == today.getDate()) {
14659                 clsName += ' today';
14660             }
14661             
14662             if (currentDate && prevMonth.valueOf() === currentDate) {
14663                 clsName += ' active';
14664             }
14665             
14666             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14667                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14668                     clsName += ' disabled';
14669             }
14670             
14671             fillMonths.cn.push({
14672                 tag: 'td',
14673                 cls: 'day ' + clsName,
14674                 html: prevMonth.getDate()
14675             })
14676             
14677             prevMonth.setDate(prevMonth.getDate()+1);
14678         }
14679           
14680         var currentYear = this.date && this.date.getUTCFullYear();
14681         var currentMonth = this.date && this.date.getUTCMonth();
14682         
14683         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14684         
14685         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14686             v.removeClass('active');
14687             
14688             if(currentYear === year && k === currentMonth){
14689                 v.addClass('active');
14690             }
14691             
14692             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14693                 v.addClass('disabled');
14694             }
14695             
14696         });
14697         
14698         
14699         year = parseInt(year/10, 10) * 10;
14700         
14701         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14702         
14703         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14704         
14705         year -= 1;
14706         for (var i = -1; i < 11; i++) {
14707             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14708                 tag: 'span',
14709                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14710                 html: year
14711             })
14712             
14713             year += 1;
14714         }
14715     },
14716     
14717     showMode: function(dir) 
14718     {
14719         if (dir) {
14720             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14721         }
14722         Roo.each(this.picker().select('>div',true).elements, function(v){
14723             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14724             v.hide();
14725         });
14726         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14727     },
14728     
14729     place: function()
14730     {
14731         if(this.isInline) return;
14732         
14733         this.picker().removeClass(['bottom', 'top']);
14734         
14735         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14736             /*
14737              * place to the top of element!
14738              *
14739              */
14740             
14741             this.picker().addClass('top');
14742             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14743             
14744             return;
14745         }
14746         
14747         this.picker().addClass('bottom');
14748         
14749         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14750     },
14751     
14752     parseDate : function(value)
14753     {
14754         if(!value || value instanceof Date){
14755             return value;
14756         }
14757         var v = Date.parseDate(value, this.format);
14758         if (!v && this.useIso) {
14759             v = Date.parseDate(value, 'Y-m-d');
14760         }
14761         if(!v && this.altFormats){
14762             if(!this.altFormatsArray){
14763                 this.altFormatsArray = this.altFormats.split("|");
14764             }
14765             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14766                 v = Date.parseDate(value, this.altFormatsArray[i]);
14767             }
14768         }
14769         return v;
14770     },
14771     
14772     formatDate : function(date, fmt)
14773     {
14774         return (!date || !(date instanceof Date)) ?
14775         date : date.dateFormat(fmt || this.format);
14776     },
14777     
14778     onFocus : function()
14779     {
14780         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14781         this.show();
14782     },
14783     
14784     onBlur : function()
14785     {
14786         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14787         
14788         var d = this.inputEl().getValue();
14789         
14790         this.setValue(d);
14791                 
14792         this.hide();
14793     },
14794     
14795     show : function()
14796     {
14797         this.picker().show();
14798         this.update();
14799         this.place();
14800         
14801         this.fireEvent('show', this, this.date);
14802     },
14803     
14804     hide : function()
14805     {
14806         if(this.isInline) return;
14807         this.picker().hide();
14808         this.viewMode = this.startViewMode;
14809         this.showMode();
14810         
14811         this.fireEvent('hide', this, this.date);
14812         
14813     },
14814     
14815     onMousedown: function(e)
14816     {
14817         e.stopPropagation();
14818         e.preventDefault();
14819     },
14820     
14821     keyup: function(e)
14822     {
14823         Roo.bootstrap.DateField.superclass.keyup.call(this);
14824         this.update();
14825     },
14826
14827     setValue: function(v)
14828     {
14829         var d = new Date(v).clearTime();
14830         
14831         if(isNaN(d.getTime())){
14832             this.date = this.viewDate = '';
14833             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14834             return;
14835         }
14836         
14837         v = this.formatDate(d);
14838         
14839         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14840         
14841         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14842      
14843         this.update();
14844
14845         this.fireEvent('select', this, this.date);
14846         
14847     },
14848     
14849     getValue: function()
14850     {
14851         return this.formatDate(this.date);
14852     },
14853     
14854     fireKey: function(e)
14855     {
14856         if (!this.picker().isVisible()){
14857             if (e.keyCode == 27) // allow escape to hide and re-show picker
14858                 this.show();
14859             return;
14860         }
14861         
14862         var dateChanged = false,
14863         dir, day, month,
14864         newDate, newViewDate;
14865         
14866         switch(e.keyCode){
14867             case 27: // escape
14868                 this.hide();
14869                 e.preventDefault();
14870                 break;
14871             case 37: // left
14872             case 39: // right
14873                 if (!this.keyboardNavigation) break;
14874                 dir = e.keyCode == 37 ? -1 : 1;
14875                 
14876                 if (e.ctrlKey){
14877                     newDate = this.moveYear(this.date, dir);
14878                     newViewDate = this.moveYear(this.viewDate, dir);
14879                 } else if (e.shiftKey){
14880                     newDate = this.moveMonth(this.date, dir);
14881                     newViewDate = this.moveMonth(this.viewDate, dir);
14882                 } else {
14883                     newDate = new Date(this.date);
14884                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14885                     newViewDate = new Date(this.viewDate);
14886                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14887                 }
14888                 if (this.dateWithinRange(newDate)){
14889                     this.date = newDate;
14890                     this.viewDate = newViewDate;
14891                     this.setValue(this.formatDate(this.date));
14892 //                    this.update();
14893                     e.preventDefault();
14894                     dateChanged = true;
14895                 }
14896                 break;
14897             case 38: // up
14898             case 40: // down
14899                 if (!this.keyboardNavigation) break;
14900                 dir = e.keyCode == 38 ? -1 : 1;
14901                 if (e.ctrlKey){
14902                     newDate = this.moveYear(this.date, dir);
14903                     newViewDate = this.moveYear(this.viewDate, dir);
14904                 } else if (e.shiftKey){
14905                     newDate = this.moveMonth(this.date, dir);
14906                     newViewDate = this.moveMonth(this.viewDate, dir);
14907                 } else {
14908                     newDate = new Date(this.date);
14909                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14910                     newViewDate = new Date(this.viewDate);
14911                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14912                 }
14913                 if (this.dateWithinRange(newDate)){
14914                     this.date = newDate;
14915                     this.viewDate = newViewDate;
14916                     this.setValue(this.formatDate(this.date));
14917 //                    this.update();
14918                     e.preventDefault();
14919                     dateChanged = true;
14920                 }
14921                 break;
14922             case 13: // enter
14923                 this.setValue(this.formatDate(this.date));
14924                 this.hide();
14925                 e.preventDefault();
14926                 break;
14927             case 9: // tab
14928                 this.setValue(this.formatDate(this.date));
14929                 this.hide();
14930                 break;
14931             case 16: // shift
14932             case 17: // ctrl
14933             case 18: // alt
14934                 break;
14935             default :
14936                 this.hide();
14937                 
14938         }
14939     },
14940     
14941     
14942     onClick: function(e) 
14943     {
14944         e.stopPropagation();
14945         e.preventDefault();
14946         
14947         var target = e.getTarget();
14948         
14949         if(target.nodeName.toLowerCase() === 'i'){
14950             target = Roo.get(target).dom.parentNode;
14951         }
14952         
14953         var nodeName = target.nodeName;
14954         var className = target.className;
14955         var html = target.innerHTML;
14956         
14957         switch(nodeName.toLowerCase()) {
14958             case 'th':
14959                 switch(className) {
14960                     case 'switch':
14961                         this.showMode(1);
14962                         break;
14963                     case 'prev':
14964                     case 'next':
14965                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14966                         switch(this.viewMode){
14967                                 case 0:
14968                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14969                                         break;
14970                                 case 1:
14971                                 case 2:
14972                                         this.viewDate = this.moveYear(this.viewDate, dir);
14973                                         break;
14974                         }
14975                         this.fill();
14976                         break;
14977                     case 'today':
14978                         var date = new Date();
14979                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14980 //                        this.fill()
14981                         this.setValue(this.formatDate(this.date));
14982                         
14983                         this.hide();
14984                         break;
14985                 }
14986                 break;
14987             case 'span':
14988                 if (className.indexOf('disabled') === -1) {
14989                     this.viewDate.setUTCDate(1);
14990                     if (className.indexOf('month') !== -1) {
14991                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14992                     } else {
14993                         var year = parseInt(html, 10) || 0;
14994                         this.viewDate.setUTCFullYear(year);
14995                         
14996                     }
14997                     this.showMode(-1);
14998                     this.fill();
14999                 }
15000                 break;
15001                 
15002             case 'td':
15003                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15004                     var day = parseInt(html, 10) || 1;
15005                     var year = this.viewDate.getUTCFullYear(),
15006                         month = this.viewDate.getUTCMonth();
15007
15008                     if (className.indexOf('old') !== -1) {
15009                         if(month === 0 ){
15010                             month = 11;
15011                             year -= 1;
15012                         }else{
15013                             month -= 1;
15014                         }
15015                     } else if (className.indexOf('new') !== -1) {
15016                         if (month == 11) {
15017                             month = 0;
15018                             year += 1;
15019                         } else {
15020                             month += 1;
15021                         }
15022                     }
15023                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15024                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15025 //                    this.fill();
15026                     this.setValue(this.formatDate(this.date));
15027                     this.hide();
15028                 }
15029                 break;
15030         }
15031     },
15032     
15033     setStartDate: function(startDate)
15034     {
15035         this.startDate = startDate || -Infinity;
15036         if (this.startDate !== -Infinity) {
15037             this.startDate = this.parseDate(this.startDate);
15038         }
15039         this.update();
15040         this.updateNavArrows();
15041     },
15042
15043     setEndDate: function(endDate)
15044     {
15045         this.endDate = endDate || Infinity;
15046         if (this.endDate !== Infinity) {
15047             this.endDate = this.parseDate(this.endDate);
15048         }
15049         this.update();
15050         this.updateNavArrows();
15051     },
15052     
15053     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15054     {
15055         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15056         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15057             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15058         }
15059         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15060             return parseInt(d, 10);
15061         });
15062         this.update();
15063         this.updateNavArrows();
15064     },
15065     
15066     updateNavArrows: function() 
15067     {
15068         var d = new Date(this.viewDate),
15069         year = d.getUTCFullYear(),
15070         month = d.getUTCMonth();
15071         
15072         Roo.each(this.picker().select('.prev', true).elements, function(v){
15073             v.show();
15074             switch (this.viewMode) {
15075                 case 0:
15076
15077                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15078                         v.hide();
15079                     }
15080                     break;
15081                 case 1:
15082                 case 2:
15083                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15084                         v.hide();
15085                     }
15086                     break;
15087             }
15088         });
15089         
15090         Roo.each(this.picker().select('.next', true).elements, function(v){
15091             v.show();
15092             switch (this.viewMode) {
15093                 case 0:
15094
15095                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15096                         v.hide();
15097                     }
15098                     break;
15099                 case 1:
15100                 case 2:
15101                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15102                         v.hide();
15103                     }
15104                     break;
15105             }
15106         })
15107     },
15108     
15109     moveMonth: function(date, dir)
15110     {
15111         if (!dir) return date;
15112         var new_date = new Date(date.valueOf()),
15113         day = new_date.getUTCDate(),
15114         month = new_date.getUTCMonth(),
15115         mag = Math.abs(dir),
15116         new_month, test;
15117         dir = dir > 0 ? 1 : -1;
15118         if (mag == 1){
15119             test = dir == -1
15120             // If going back one month, make sure month is not current month
15121             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15122             ? function(){
15123                 return new_date.getUTCMonth() == month;
15124             }
15125             // If going forward one month, make sure month is as expected
15126             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15127             : function(){
15128                 return new_date.getUTCMonth() != new_month;
15129             };
15130             new_month = month + dir;
15131             new_date.setUTCMonth(new_month);
15132             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15133             if (new_month < 0 || new_month > 11)
15134                 new_month = (new_month + 12) % 12;
15135         } else {
15136             // For magnitudes >1, move one month at a time...
15137             for (var i=0; i<mag; i++)
15138                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15139                 new_date = this.moveMonth(new_date, dir);
15140             // ...then reset the day, keeping it in the new month
15141             new_month = new_date.getUTCMonth();
15142             new_date.setUTCDate(day);
15143             test = function(){
15144                 return new_month != new_date.getUTCMonth();
15145             };
15146         }
15147         // Common date-resetting loop -- if date is beyond end of month, make it
15148         // end of month
15149         while (test()){
15150             new_date.setUTCDate(--day);
15151             new_date.setUTCMonth(new_month);
15152         }
15153         return new_date;
15154     },
15155
15156     moveYear: function(date, dir)
15157     {
15158         return this.moveMonth(date, dir*12);
15159     },
15160
15161     dateWithinRange: function(date)
15162     {
15163         return date >= this.startDate && date <= this.endDate;
15164     },
15165
15166     
15167     remove: function() 
15168     {
15169         this.picker().remove();
15170     }
15171    
15172 });
15173
15174 Roo.apply(Roo.bootstrap.DateField,  {
15175     
15176     head : {
15177         tag: 'thead',
15178         cn: [
15179         {
15180             tag: 'tr',
15181             cn: [
15182             {
15183                 tag: 'th',
15184                 cls: 'prev',
15185                 html: '<i class="fa fa-arrow-left"/>'
15186             },
15187             {
15188                 tag: 'th',
15189                 cls: 'switch',
15190                 colspan: '5'
15191             },
15192             {
15193                 tag: 'th',
15194                 cls: 'next',
15195                 html: '<i class="fa fa-arrow-right"/>'
15196             }
15197
15198             ]
15199         }
15200         ]
15201     },
15202     
15203     content : {
15204         tag: 'tbody',
15205         cn: [
15206         {
15207             tag: 'tr',
15208             cn: [
15209             {
15210                 tag: 'td',
15211                 colspan: '7'
15212             }
15213             ]
15214         }
15215         ]
15216     },
15217     
15218     footer : {
15219         tag: 'tfoot',
15220         cn: [
15221         {
15222             tag: 'tr',
15223             cn: [
15224             {
15225                 tag: 'th',
15226                 colspan: '7',
15227                 cls: 'today'
15228             }
15229                     
15230             ]
15231         }
15232         ]
15233     },
15234     
15235     dates:{
15236         en: {
15237             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15238             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15239             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15240             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15241             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15242             today: "Today"
15243         }
15244     },
15245     
15246     modes: [
15247     {
15248         clsName: 'days',
15249         navFnc: 'Month',
15250         navStep: 1
15251     },
15252     {
15253         clsName: 'months',
15254         navFnc: 'FullYear',
15255         navStep: 1
15256     },
15257     {
15258         clsName: 'years',
15259         navFnc: 'FullYear',
15260         navStep: 10
15261     }]
15262 });
15263
15264 Roo.apply(Roo.bootstrap.DateField,  {
15265   
15266     template : {
15267         tag: 'div',
15268         cls: 'datepicker dropdown-menu',
15269         cn: [
15270         {
15271             tag: 'div',
15272             cls: 'datepicker-days',
15273             cn: [
15274             {
15275                 tag: 'table',
15276                 cls: 'table-condensed',
15277                 cn:[
15278                 Roo.bootstrap.DateField.head,
15279                 {
15280                     tag: 'tbody'
15281                 },
15282                 Roo.bootstrap.DateField.footer
15283                 ]
15284             }
15285             ]
15286         },
15287         {
15288             tag: 'div',
15289             cls: 'datepicker-months',
15290             cn: [
15291             {
15292                 tag: 'table',
15293                 cls: 'table-condensed',
15294                 cn:[
15295                 Roo.bootstrap.DateField.head,
15296                 Roo.bootstrap.DateField.content,
15297                 Roo.bootstrap.DateField.footer
15298                 ]
15299             }
15300             ]
15301         },
15302         {
15303             tag: 'div',
15304             cls: 'datepicker-years',
15305             cn: [
15306             {
15307                 tag: 'table',
15308                 cls: 'table-condensed',
15309                 cn:[
15310                 Roo.bootstrap.DateField.head,
15311                 Roo.bootstrap.DateField.content,
15312                 Roo.bootstrap.DateField.footer
15313                 ]
15314             }
15315             ]
15316         }
15317         ]
15318     }
15319 });
15320
15321  
15322
15323  /*
15324  * - LGPL
15325  *
15326  * TimeField
15327  * 
15328  */
15329
15330 /**
15331  * @class Roo.bootstrap.TimeField
15332  * @extends Roo.bootstrap.Input
15333  * Bootstrap DateField class
15334  * 
15335  * 
15336  * @constructor
15337  * Create a new TimeField
15338  * @param {Object} config The config object
15339  */
15340
15341 Roo.bootstrap.TimeField = function(config){
15342     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15343     this.addEvents({
15344             /**
15345              * @event show
15346              * Fires when this field show.
15347              * @param {Roo.bootstrap.DateField} this
15348              * @param {Mixed} date The date value
15349              */
15350             show : true,
15351             /**
15352              * @event show
15353              * Fires when this field hide.
15354              * @param {Roo.bootstrap.DateField} this
15355              * @param {Mixed} date The date value
15356              */
15357             hide : true,
15358             /**
15359              * @event select
15360              * Fires when select a date.
15361              * @param {Roo.bootstrap.DateField} this
15362              * @param {Mixed} date The date value
15363              */
15364             select : true
15365         });
15366 };
15367
15368 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15369     
15370     /**
15371      * @cfg {String} format
15372      * The default time format string which can be overriden for localization support.  The format must be
15373      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15374      */
15375     format : "H:i",
15376        
15377     onRender: function(ct, position)
15378     {
15379         
15380         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15381                 
15382         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15383         
15384         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15385         
15386         this.pop = this.picker().select('>.datepicker-time',true).first();
15387         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15388         
15389         this.picker().on('mousedown', this.onMousedown, this);
15390         this.picker().on('click', this.onClick, this);
15391         
15392         this.picker().addClass('datepicker-dropdown');
15393     
15394         this.fillTime();
15395         this.update();
15396             
15397         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15398         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15399         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15400         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15401         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15402         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15403
15404     },
15405     
15406     fireKey: function(e){
15407         if (!this.picker().isVisible()){
15408             if (e.keyCode == 27) // allow escape to hide and re-show picker
15409                 this.show();
15410             return;
15411         }
15412
15413         e.preventDefault();
15414         
15415         switch(e.keyCode){
15416             case 27: // escape
15417                 this.hide();
15418                 break;
15419             case 37: // left
15420             case 39: // right
15421                 this.onTogglePeriod();
15422                 break;
15423             case 38: // up
15424                 this.onIncrementMinutes();
15425                 break;
15426             case 40: // down
15427                 this.onDecrementMinutes();
15428                 break;
15429             case 13: // enter
15430             case 9: // tab
15431                 this.setTime();
15432                 break;
15433         }
15434     },
15435     
15436     onClick: function(e) {
15437         e.stopPropagation();
15438         e.preventDefault();
15439     },
15440     
15441     picker : function()
15442     {
15443         return this.el.select('.datepicker', true).first();
15444     },
15445     
15446     fillTime: function()
15447     {    
15448         var time = this.pop.select('tbody', true).first();
15449         
15450         time.dom.innerHTML = '';
15451         
15452         time.createChild({
15453             tag: 'tr',
15454             cn: [
15455                 {
15456                     tag: 'td',
15457                     cn: [
15458                         {
15459                             tag: 'a',
15460                             href: '#',
15461                             cls: 'btn',
15462                             cn: [
15463                                 {
15464                                     tag: 'span',
15465                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15466                                 }
15467                             ]
15468                         } 
15469                     ]
15470                 },
15471                 {
15472                     tag: 'td',
15473                     cls: 'separator'
15474                 },
15475                 {
15476                     tag: 'td',
15477                     cn: [
15478                         {
15479                             tag: 'a',
15480                             href: '#',
15481                             cls: 'btn',
15482                             cn: [
15483                                 {
15484                                     tag: 'span',
15485                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15486                                 }
15487                             ]
15488                         }
15489                     ]
15490                 },
15491                 {
15492                     tag: 'td',
15493                     cls: 'separator'
15494                 }
15495             ]
15496         });
15497         
15498         time.createChild({
15499             tag: 'tr',
15500             cn: [
15501                 {
15502                     tag: 'td',
15503                     cn: [
15504                         {
15505                             tag: 'span',
15506                             cls: 'timepicker-hour',
15507                             html: '00'
15508                         }  
15509                     ]
15510                 },
15511                 {
15512                     tag: 'td',
15513                     cls: 'separator',
15514                     html: ':'
15515                 },
15516                 {
15517                     tag: 'td',
15518                     cn: [
15519                         {
15520                             tag: 'span',
15521                             cls: 'timepicker-minute',
15522                             html: '00'
15523                         }  
15524                     ]
15525                 },
15526                 {
15527                     tag: 'td',
15528                     cls: 'separator'
15529                 },
15530                 {
15531                     tag: 'td',
15532                     cn: [
15533                         {
15534                             tag: 'button',
15535                             type: 'button',
15536                             cls: 'btn btn-primary period',
15537                             html: 'AM'
15538                             
15539                         }
15540                     ]
15541                 }
15542             ]
15543         });
15544         
15545         time.createChild({
15546             tag: 'tr',
15547             cn: [
15548                 {
15549                     tag: 'td',
15550                     cn: [
15551                         {
15552                             tag: 'a',
15553                             href: '#',
15554                             cls: 'btn',
15555                             cn: [
15556                                 {
15557                                     tag: 'span',
15558                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15559                                 }
15560                             ]
15561                         }
15562                     ]
15563                 },
15564                 {
15565                     tag: 'td',
15566                     cls: 'separator'
15567                 },
15568                 {
15569                     tag: 'td',
15570                     cn: [
15571                         {
15572                             tag: 'a',
15573                             href: '#',
15574                             cls: 'btn',
15575                             cn: [
15576                                 {
15577                                     tag: 'span',
15578                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15579                                 }
15580                             ]
15581                         }
15582                     ]
15583                 },
15584                 {
15585                     tag: 'td',
15586                     cls: 'separator'
15587                 }
15588             ]
15589         });
15590         
15591     },
15592     
15593     update: function()
15594     {
15595         
15596         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15597         
15598         this.fill();
15599     },
15600     
15601     fill: function() 
15602     {
15603         var hours = this.time.getHours();
15604         var minutes = this.time.getMinutes();
15605         var period = 'AM';
15606         
15607         if(hours > 11){
15608             period = 'PM';
15609         }
15610         
15611         if(hours == 0){
15612             hours = 12;
15613         }
15614         
15615         
15616         if(hours > 12){
15617             hours = hours - 12;
15618         }
15619         
15620         if(hours < 10){
15621             hours = '0' + hours;
15622         }
15623         
15624         if(minutes < 10){
15625             minutes = '0' + minutes;
15626         }
15627         
15628         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15629         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15630         this.pop.select('button', true).first().dom.innerHTML = period;
15631         
15632     },
15633     
15634     place: function()
15635     {   
15636         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15637         
15638         var cls = ['bottom'];
15639         
15640         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15641             cls.pop();
15642             cls.push('top');
15643         }
15644         
15645         cls.push('right');
15646         
15647         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15648             cls.pop();
15649             cls.push('left');
15650         }
15651         
15652         this.picker().addClass(cls.join('-'));
15653         
15654         var _this = this;
15655         
15656         Roo.each(cls, function(c){
15657             if(c == 'bottom'){
15658                 _this.picker().setTop(_this.inputEl().getHeight());
15659                 return;
15660             }
15661             if(c == 'top'){
15662                 _this.picker().setTop(0 - _this.picker().getHeight());
15663                 return;
15664             }
15665             
15666             if(c == 'left'){
15667                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15668                 return;
15669             }
15670             if(c == 'right'){
15671                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15672                 return;
15673             }
15674         });
15675         
15676     },
15677   
15678     onFocus : function()
15679     {
15680         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15681         this.show();
15682     },
15683     
15684     onBlur : function()
15685     {
15686         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15687         this.hide();
15688     },
15689     
15690     show : function()
15691     {
15692         this.picker().show();
15693         this.pop.show();
15694         this.update();
15695         this.place();
15696         
15697         this.fireEvent('show', this, this.date);
15698     },
15699     
15700     hide : function()
15701     {
15702         this.picker().hide();
15703         this.pop.hide();
15704         
15705         this.fireEvent('hide', this, this.date);
15706     },
15707     
15708     setTime : function()
15709     {
15710         this.hide();
15711         this.setValue(this.time.format(this.format));
15712         
15713         this.fireEvent('select', this, this.date);
15714         
15715         
15716     },
15717     
15718     onMousedown: function(e){
15719         e.stopPropagation();
15720         e.preventDefault();
15721     },
15722     
15723     onIncrementHours: function()
15724     {
15725         Roo.log('onIncrementHours');
15726         this.time = this.time.add(Date.HOUR, 1);
15727         this.update();
15728         
15729     },
15730     
15731     onDecrementHours: function()
15732     {
15733         Roo.log('onDecrementHours');
15734         this.time = this.time.add(Date.HOUR, -1);
15735         this.update();
15736     },
15737     
15738     onIncrementMinutes: function()
15739     {
15740         Roo.log('onIncrementMinutes');
15741         this.time = this.time.add(Date.MINUTE, 1);
15742         this.update();
15743     },
15744     
15745     onDecrementMinutes: function()
15746     {
15747         Roo.log('onDecrementMinutes');
15748         this.time = this.time.add(Date.MINUTE, -1);
15749         this.update();
15750     },
15751     
15752     onTogglePeriod: function()
15753     {
15754         Roo.log('onTogglePeriod');
15755         this.time = this.time.add(Date.HOUR, 12);
15756         this.update();
15757     }
15758     
15759    
15760 });
15761
15762 Roo.apply(Roo.bootstrap.TimeField,  {
15763     
15764     content : {
15765         tag: 'tbody',
15766         cn: [
15767             {
15768                 tag: 'tr',
15769                 cn: [
15770                 {
15771                     tag: 'td',
15772                     colspan: '7'
15773                 }
15774                 ]
15775             }
15776         ]
15777     },
15778     
15779     footer : {
15780         tag: 'tfoot',
15781         cn: [
15782             {
15783                 tag: 'tr',
15784                 cn: [
15785                 {
15786                     tag: 'th',
15787                     colspan: '7',
15788                     cls: '',
15789                     cn: [
15790                         {
15791                             tag: 'button',
15792                             cls: 'btn btn-info ok',
15793                             html: 'OK'
15794                         }
15795                     ]
15796                 }
15797
15798                 ]
15799             }
15800         ]
15801     }
15802 });
15803
15804 Roo.apply(Roo.bootstrap.TimeField,  {
15805   
15806     template : {
15807         tag: 'div',
15808         cls: 'datepicker dropdown-menu',
15809         cn: [
15810             {
15811                 tag: 'div',
15812                 cls: 'datepicker-time',
15813                 cn: [
15814                 {
15815                     tag: 'table',
15816                     cls: 'table-condensed',
15817                     cn:[
15818                     Roo.bootstrap.TimeField.content,
15819                     Roo.bootstrap.TimeField.footer
15820                     ]
15821                 }
15822                 ]
15823             }
15824         ]
15825     }
15826 });
15827
15828  
15829
15830  /*
15831  * - LGPL
15832  *
15833  * CheckBox
15834  * 
15835  */
15836
15837 /**
15838  * @class Roo.bootstrap.CheckBox
15839  * @extends Roo.bootstrap.Input
15840  * Bootstrap CheckBox class
15841  * 
15842  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15843  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15844  * @cfg {String} boxLabel The text that appears beside the checkbox
15845  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15846  * @cfg {Boolean} checked initnal the element
15847  * 
15848  * 
15849  * @constructor
15850  * Create a new CheckBox
15851  * @param {Object} config The config object
15852  */
15853
15854 Roo.bootstrap.CheckBox = function(config){
15855     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15856    
15857         this.addEvents({
15858             /**
15859             * @event check
15860             * Fires when the element is checked or unchecked.
15861             * @param {Roo.bootstrap.CheckBox} this This input
15862             * @param {Boolean} checked The new checked value
15863             */
15864            check : true
15865         });
15866 };
15867
15868 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15869     
15870     inputType: 'checkbox',
15871     inputValue: 1,
15872     valueOff: 0,
15873     boxLabel: false,
15874     checked: false,
15875     weight : false,
15876     
15877     getAutoCreate : function()
15878     {
15879         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15880         
15881         var id = Roo.id();
15882         
15883         var cfg = {};
15884         
15885         cfg.cls = 'form-group checkbox' //input-group
15886         
15887         
15888         
15889         
15890         var input =  {
15891             tag: 'input',
15892             id : id,
15893             type : this.inputType,
15894             value : (!this.checked) ? this.valueOff : this.inputValue,
15895             cls : 'roo-checkbox', //'form-box',
15896             placeholder : this.placeholder || ''
15897             
15898         };
15899         
15900         if (this.weight) { // Validity check?
15901             cfg.cls += " checkbox-" + this.weight;
15902         }
15903         
15904         if (this.disabled) {
15905             input.disabled=true;
15906         }
15907         
15908         if(this.checked){
15909             input.checked = this.checked;
15910         }
15911         
15912         if (this.name) {
15913             input.name = this.name;
15914         }
15915         
15916         if (this.size) {
15917             input.cls += ' input-' + this.size;
15918         }
15919         
15920         var settings=this;
15921         ['xs','sm','md','lg'].map(function(size){
15922             if (settings[size]) {
15923                 cfg.cls += ' col-' + size + '-' + settings[size];
15924             }
15925         });
15926         
15927        
15928         
15929         var inputblock = input;
15930         
15931         
15932         
15933         
15934         if (this.before || this.after) {
15935             
15936             inputblock = {
15937                 cls : 'input-group',
15938                 cn :  [] 
15939             };
15940             if (this.before) {
15941                 inputblock.cn.push({
15942                     tag :'span',
15943                     cls : 'input-group-addon',
15944                     html : this.before
15945                 });
15946             }
15947             inputblock.cn.push(input);
15948             if (this.after) {
15949                 inputblock.cn.push({
15950                     tag :'span',
15951                     cls : 'input-group-addon',
15952                     html : this.after
15953                 });
15954             }
15955             
15956         };
15957         
15958         if (align ==='left' && this.fieldLabel.length) {
15959                 Roo.log("left and has label");
15960                 cfg.cn = [
15961                     
15962                     {
15963                         tag: 'label',
15964                         'for' :  id,
15965                         cls : 'control-label col-md-' + this.labelWidth,
15966                         html : this.fieldLabel
15967                         
15968                     },
15969                     {
15970                         cls : "col-md-" + (12 - this.labelWidth), 
15971                         cn: [
15972                             inputblock
15973                         ]
15974                     }
15975                     
15976                 ];
15977         } else if ( this.fieldLabel.length) {
15978                 Roo.log(" label");
15979                 cfg.cn = [
15980                    
15981                     {
15982                         tag: this.boxLabel ? 'span' : 'label',
15983                         'for': id,
15984                         cls: 'control-label box-input-label',
15985                         //cls : 'input-group-addon',
15986                         html : this.fieldLabel
15987                         
15988                     },
15989                     
15990                     inputblock
15991                     
15992                 ];
15993
15994         } else {
15995             
15996                 Roo.log(" no label && no align");
15997                 cfg.cn = [  inputblock ] ;
15998                 
15999                 
16000         };
16001          if(this.boxLabel){
16002             cfg.cn.push( {
16003                 tag: 'label',
16004                 'for': id,
16005                 cls: 'box-label',
16006                 html: this.boxLabel
16007                 
16008             });
16009         }
16010         
16011         
16012        
16013         return cfg;
16014         
16015     },
16016     
16017     /**
16018      * return the real input element.
16019      */
16020     inputEl: function ()
16021     {
16022         return this.el.select('input.roo-checkbox',true).first();
16023     },
16024     
16025     label: function()
16026     {
16027         return this.el.select('label.control-label',true).first();
16028     },
16029     
16030     initEvents : function()
16031     {
16032 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16033         
16034         this.inputEl().on('click', this.onClick,  this);
16035         
16036     },
16037     
16038     onClick : function()
16039     {   
16040         this.setChecked(!this.checked);
16041     },
16042     
16043     setChecked : function(state,suppressEvent)
16044     {
16045         this.checked = state;
16046         
16047         this.inputEl().dom.checked = state;
16048         
16049         if(suppressEvent !== true){
16050             this.fireEvent('check', this, state);
16051         }
16052         
16053         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16054         
16055     },
16056     
16057     setValue : function(v,suppressEvent)
16058     {
16059         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16060     }
16061     
16062 });
16063
16064  
16065 /*
16066  * - LGPL
16067  *
16068  * Radio
16069  * 
16070  */
16071
16072 /**
16073  * @class Roo.bootstrap.Radio
16074  * @extends Roo.bootstrap.CheckBox
16075  * Bootstrap Radio class
16076
16077  * @constructor
16078  * Create a new Radio
16079  * @param {Object} config The config object
16080  */
16081
16082 Roo.bootstrap.Radio = function(config){
16083     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16084    
16085 };
16086
16087 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16088     
16089     inputType: 'radio',
16090     inputValue: '',
16091     valueOff: '',
16092     
16093     getAutoCreate : function()
16094     {
16095         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16096         
16097         var id = Roo.id();
16098         
16099         var cfg = {};
16100         
16101         cfg.cls = 'form-group radio' //input-group
16102         
16103         var input =  {
16104             tag: 'input',
16105             id : id,
16106             type : this.inputType,
16107             value : (!this.checked) ? this.valueOff : this.inputValue,
16108             cls : 'roo-radio',
16109             placeholder : this.placeholder || ''
16110             
16111         };
16112           if (this.weight) { // Validity check?
16113             cfg.cls += " radio-" + this.weight;
16114         }
16115         if (this.disabled) {
16116             input.disabled=true;
16117         }
16118         
16119         if(this.checked){
16120             input.checked = this.checked;
16121         }
16122         
16123         if (this.name) {
16124             input.name = this.name;
16125         }
16126         
16127         if (this.size) {
16128             input.cls += ' input-' + this.size;
16129         }
16130         
16131         var settings=this;
16132         ['xs','sm','md','lg'].map(function(size){
16133             if (settings[size]) {
16134                 cfg.cls += ' col-' + size + '-' + settings[size];
16135             }
16136         });
16137         
16138         var inputblock = input;
16139         
16140         if (this.before || this.after) {
16141             
16142             inputblock = {
16143                 cls : 'input-group',
16144                 cn :  [] 
16145             };
16146             if (this.before) {
16147                 inputblock.cn.push({
16148                     tag :'span',
16149                     cls : 'input-group-addon',
16150                     html : this.before
16151                 });
16152             }
16153             inputblock.cn.push(input);
16154             if (this.after) {
16155                 inputblock.cn.push({
16156                     tag :'span',
16157                     cls : 'input-group-addon',
16158                     html : this.after
16159                 });
16160             }
16161             
16162         };
16163         
16164         if (align ==='left' && this.fieldLabel.length) {
16165                 Roo.log("left and has label");
16166                 cfg.cn = [
16167                     
16168                     {
16169                         tag: 'label',
16170                         'for' :  id,
16171                         cls : 'control-label col-md-' + this.labelWidth,
16172                         html : this.fieldLabel
16173                         
16174                     },
16175                     {
16176                         cls : "col-md-" + (12 - this.labelWidth), 
16177                         cn: [
16178                             inputblock
16179                         ]
16180                     }
16181                     
16182                 ];
16183         } else if ( this.fieldLabel.length) {
16184                 Roo.log(" label");
16185                  cfg.cn = [
16186                    
16187                     {
16188                         tag: 'label',
16189                         'for': id,
16190                         cls: 'control-label box-input-label',
16191                         //cls : 'input-group-addon',
16192                         html : this.fieldLabel
16193                         
16194                     },
16195                     
16196                     inputblock
16197                     
16198                 ];
16199
16200         } else {
16201             
16202                    Roo.log(" no label && no align");
16203                 cfg.cn = [
16204                     
16205                         inputblock
16206                     
16207                 ];
16208                 
16209                 
16210         };
16211         
16212         if(this.boxLabel){
16213             cfg.cn.push({
16214                 tag: 'label',
16215                 'for': id,
16216                 cls: 'box-label',
16217                 html: this.boxLabel
16218             })
16219         }
16220         
16221         return cfg;
16222         
16223     },
16224     inputEl: function ()
16225     {
16226         return this.el.select('input.roo-radio',true).first();
16227     },
16228     onClick : function()
16229     {   
16230         this.setChecked(true);
16231     },
16232     
16233     setChecked : function(state,suppressEvent)
16234     {
16235         if(state){
16236             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16237                 v.dom.checked = false;
16238             });
16239         }
16240         
16241         this.checked = state;
16242         this.inputEl().dom.checked = state;
16243         
16244         if(suppressEvent !== true){
16245             this.fireEvent('check', this, state);
16246         }
16247         
16248         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16249         
16250     },
16251     
16252     getGroupValue : function()
16253     {
16254         var value = ''
16255         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16256             if(v.dom.checked == true){
16257                 value = v.dom.value;
16258             }
16259         });
16260         
16261         return value;
16262     },
16263     
16264     /**
16265      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16266      * @return {Mixed} value The field value
16267      */
16268     getValue : function(){
16269         return this.getGroupValue();
16270     }
16271     
16272 });
16273
16274  
16275 //<script type="text/javascript">
16276
16277 /*
16278  * Based  Ext JS Library 1.1.1
16279  * Copyright(c) 2006-2007, Ext JS, LLC.
16280  * LGPL
16281  *
16282  */
16283  
16284 /**
16285  * @class Roo.HtmlEditorCore
16286  * @extends Roo.Component
16287  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16288  *
16289  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16290  */
16291
16292 Roo.HtmlEditorCore = function(config){
16293     
16294     
16295     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16296     this.addEvents({
16297         /**
16298          * @event initialize
16299          * Fires when the editor is fully initialized (including the iframe)
16300          * @param {Roo.HtmlEditorCore} this
16301          */
16302         initialize: true,
16303         /**
16304          * @event activate
16305          * Fires when the editor is first receives the focus. Any insertion must wait
16306          * until after this event.
16307          * @param {Roo.HtmlEditorCore} this
16308          */
16309         activate: true,
16310          /**
16311          * @event beforesync
16312          * Fires before the textarea is updated with content from the editor iframe. Return false
16313          * to cancel the sync.
16314          * @param {Roo.HtmlEditorCore} this
16315          * @param {String} html
16316          */
16317         beforesync: true,
16318          /**
16319          * @event beforepush
16320          * Fires before the iframe editor is updated with content from the textarea. Return false
16321          * to cancel the push.
16322          * @param {Roo.HtmlEditorCore} this
16323          * @param {String} html
16324          */
16325         beforepush: true,
16326          /**
16327          * @event sync
16328          * Fires when the textarea is updated with content from the editor iframe.
16329          * @param {Roo.HtmlEditorCore} this
16330          * @param {String} html
16331          */
16332         sync: true,
16333          /**
16334          * @event push
16335          * Fires when the iframe editor is updated with content from the textarea.
16336          * @param {Roo.HtmlEditorCore} this
16337          * @param {String} html
16338          */
16339         push: true,
16340         
16341         /**
16342          * @event editorevent
16343          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16344          * @param {Roo.HtmlEditorCore} this
16345          */
16346         editorevent: true
16347     });
16348      
16349 };
16350
16351
16352 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16353
16354
16355      /**
16356      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16357      */
16358     
16359     owner : false,
16360     
16361      /**
16362      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16363      *                        Roo.resizable.
16364      */
16365     resizable : false,
16366      /**
16367      * @cfg {Number} height (in pixels)
16368      */   
16369     height: 300,
16370    /**
16371      * @cfg {Number} width (in pixels)
16372      */   
16373     width: 500,
16374     
16375     /**
16376      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16377      * 
16378      */
16379     stylesheets: false,
16380     
16381     // id of frame..
16382     frameId: false,
16383     
16384     // private properties
16385     validationEvent : false,
16386     deferHeight: true,
16387     initialized : false,
16388     activated : false,
16389     sourceEditMode : false,
16390     onFocus : Roo.emptyFn,
16391     iframePad:3,
16392     hideMode:'offsets',
16393     
16394     clearUp: true,
16395     
16396      
16397     
16398
16399     /**
16400      * Protected method that will not generally be called directly. It
16401      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16402      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16403      */
16404     getDocMarkup : function(){
16405         // body styles..
16406         var st = '';
16407         Roo.log(this.stylesheets);
16408         
16409         // inherit styels from page...?? 
16410         if (this.stylesheets === false) {
16411             
16412             Roo.get(document.head).select('style').each(function(node) {
16413                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16414             });
16415             
16416             Roo.get(document.head).select('link').each(function(node) { 
16417                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16418             });
16419             
16420         } else if (!this.stylesheets.length) {
16421                 // simple..
16422                 st = '<style type="text/css">' +
16423                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16424                    '</style>';
16425         } else {
16426             Roo.each(this.stylesheets, function(s) {
16427                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16428             });
16429             
16430         }
16431         
16432         st +=  '<style type="text/css">' +
16433             'IMG { cursor: pointer } ' +
16434         '</style>';
16435
16436         
16437         return '<html><head>' + st  +
16438             //<style type="text/css">' +
16439             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16440             //'</style>' +
16441             ' </head><body class="roo-htmleditor-body"></body></html>';
16442     },
16443
16444     // private
16445     onRender : function(ct, position)
16446     {
16447         var _t = this;
16448         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16449         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16450         
16451         
16452         this.el.dom.style.border = '0 none';
16453         this.el.dom.setAttribute('tabIndex', -1);
16454         this.el.addClass('x-hidden hide');
16455         
16456         
16457         
16458         if(Roo.isIE){ // fix IE 1px bogus margin
16459             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16460         }
16461        
16462         
16463         this.frameId = Roo.id();
16464         
16465          
16466         
16467         var iframe = this.owner.wrap.createChild({
16468             tag: 'iframe',
16469             cls: 'form-control', // bootstrap..
16470             id: this.frameId,
16471             name: this.frameId,
16472             frameBorder : 'no',
16473             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16474         }, this.el
16475         );
16476         
16477         
16478         this.iframe = iframe.dom;
16479
16480          this.assignDocWin();
16481         
16482         this.doc.designMode = 'on';
16483        
16484         this.doc.open();
16485         this.doc.write(this.getDocMarkup());
16486         this.doc.close();
16487
16488         
16489         var task = { // must defer to wait for browser to be ready
16490             run : function(){
16491                 //console.log("run task?" + this.doc.readyState);
16492                 this.assignDocWin();
16493                 if(this.doc.body || this.doc.readyState == 'complete'){
16494                     try {
16495                         this.doc.designMode="on";
16496                     } catch (e) {
16497                         return;
16498                     }
16499                     Roo.TaskMgr.stop(task);
16500                     this.initEditor.defer(10, this);
16501                 }
16502             },
16503             interval : 10,
16504             duration: 10000,
16505             scope: this
16506         };
16507         Roo.TaskMgr.start(task);
16508
16509         
16510          
16511     },
16512
16513     // private
16514     onResize : function(w, h)
16515     {
16516          Roo.log('resize: ' +w + ',' + h );
16517         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16518         if(!this.iframe){
16519             return;
16520         }
16521         if(typeof w == 'number'){
16522             
16523             this.iframe.style.width = w + 'px';
16524         }
16525         if(typeof h == 'number'){
16526             
16527             this.iframe.style.height = h + 'px';
16528             if(this.doc){
16529                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16530             }
16531         }
16532         
16533     },
16534
16535     /**
16536      * Toggles the editor between standard and source edit mode.
16537      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16538      */
16539     toggleSourceEdit : function(sourceEditMode){
16540         
16541         this.sourceEditMode = sourceEditMode === true;
16542         
16543         if(this.sourceEditMode){
16544  
16545             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16546             
16547         }else{
16548             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16549             //this.iframe.className = '';
16550             this.deferFocus();
16551         }
16552         //this.setSize(this.owner.wrap.getSize());
16553         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16554     },
16555
16556     
16557   
16558
16559     /**
16560      * Protected method that will not generally be called directly. If you need/want
16561      * custom HTML cleanup, this is the method you should override.
16562      * @param {String} html The HTML to be cleaned
16563      * return {String} The cleaned HTML
16564      */
16565     cleanHtml : function(html){
16566         html = String(html);
16567         if(html.length > 5){
16568             if(Roo.isSafari){ // strip safari nonsense
16569                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16570             }
16571         }
16572         if(html == '&nbsp;'){
16573             html = '';
16574         }
16575         return html;
16576     },
16577
16578     /**
16579      * HTML Editor -> Textarea
16580      * Protected method that will not generally be called directly. Syncs the contents
16581      * of the editor iframe with the textarea.
16582      */
16583     syncValue : function(){
16584         if(this.initialized){
16585             var bd = (this.doc.body || this.doc.documentElement);
16586             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16587             var html = bd.innerHTML;
16588             if(Roo.isSafari){
16589                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16590                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16591                 if(m && m[1]){
16592                     html = '<div style="'+m[0]+'">' + html + '</div>';
16593                 }
16594             }
16595             html = this.cleanHtml(html);
16596             // fix up the special chars.. normaly like back quotes in word...
16597             // however we do not want to do this with chinese..
16598             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16599                 var cc = b.charCodeAt();
16600                 if (
16601                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16602                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16603                     (cc >= 0xf900 && cc < 0xfb00 )
16604                 ) {
16605                         return b;
16606                 }
16607                 return "&#"+cc+";" 
16608             });
16609             if(this.owner.fireEvent('beforesync', this, html) !== false){
16610                 this.el.dom.value = html;
16611                 this.owner.fireEvent('sync', this, html);
16612             }
16613         }
16614     },
16615
16616     /**
16617      * Protected method that will not generally be called directly. Pushes the value of the textarea
16618      * into the iframe editor.
16619      */
16620     pushValue : function(){
16621         if(this.initialized){
16622             var v = this.el.dom.value.trim();
16623             
16624 //            if(v.length < 1){
16625 //                v = '&#160;';
16626 //            }
16627             
16628             if(this.owner.fireEvent('beforepush', this, v) !== false){
16629                 var d = (this.doc.body || this.doc.documentElement);
16630                 d.innerHTML = v;
16631                 this.cleanUpPaste();
16632                 this.el.dom.value = d.innerHTML;
16633                 this.owner.fireEvent('push', this, v);
16634             }
16635         }
16636     },
16637
16638     // private
16639     deferFocus : function(){
16640         this.focus.defer(10, this);
16641     },
16642
16643     // doc'ed in Field
16644     focus : function(){
16645         if(this.win && !this.sourceEditMode){
16646             this.win.focus();
16647         }else{
16648             this.el.focus();
16649         }
16650     },
16651     
16652     assignDocWin: function()
16653     {
16654         var iframe = this.iframe;
16655         
16656          if(Roo.isIE){
16657             this.doc = iframe.contentWindow.document;
16658             this.win = iframe.contentWindow;
16659         } else {
16660 //            if (!Roo.get(this.frameId)) {
16661 //                return;
16662 //            }
16663 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16664 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16665             
16666             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16667                 return;
16668             }
16669             
16670             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16671             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16672         }
16673     },
16674     
16675     // private
16676     initEditor : function(){
16677         //console.log("INIT EDITOR");
16678         this.assignDocWin();
16679         
16680         
16681         
16682         this.doc.designMode="on";
16683         this.doc.open();
16684         this.doc.write(this.getDocMarkup());
16685         this.doc.close();
16686         
16687         var dbody = (this.doc.body || this.doc.documentElement);
16688         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16689         // this copies styles from the containing element into thsi one..
16690         // not sure why we need all of this..
16691         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16692         
16693         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16694         //ss['background-attachment'] = 'fixed'; // w3c
16695         dbody.bgProperties = 'fixed'; // ie
16696         //Roo.DomHelper.applyStyles(dbody, ss);
16697         Roo.EventManager.on(this.doc, {
16698             //'mousedown': this.onEditorEvent,
16699             'mouseup': this.onEditorEvent,
16700             'dblclick': this.onEditorEvent,
16701             'click': this.onEditorEvent,
16702             'keyup': this.onEditorEvent,
16703             buffer:100,
16704             scope: this
16705         });
16706         if(Roo.isGecko){
16707             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16708         }
16709         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16710             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16711         }
16712         this.initialized = true;
16713
16714         this.owner.fireEvent('initialize', this);
16715         this.pushValue();
16716     },
16717
16718     // private
16719     onDestroy : function(){
16720         
16721         
16722         
16723         if(this.rendered){
16724             
16725             //for (var i =0; i < this.toolbars.length;i++) {
16726             //    // fixme - ask toolbars for heights?
16727             //    this.toolbars[i].onDestroy();
16728            // }
16729             
16730             //this.wrap.dom.innerHTML = '';
16731             //this.wrap.remove();
16732         }
16733     },
16734
16735     // private
16736     onFirstFocus : function(){
16737         
16738         this.assignDocWin();
16739         
16740         
16741         this.activated = true;
16742          
16743     
16744         if(Roo.isGecko){ // prevent silly gecko errors
16745             this.win.focus();
16746             var s = this.win.getSelection();
16747             if(!s.focusNode || s.focusNode.nodeType != 3){
16748                 var r = s.getRangeAt(0);
16749                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16750                 r.collapse(true);
16751                 this.deferFocus();
16752             }
16753             try{
16754                 this.execCmd('useCSS', true);
16755                 this.execCmd('styleWithCSS', false);
16756             }catch(e){}
16757         }
16758         this.owner.fireEvent('activate', this);
16759     },
16760
16761     // private
16762     adjustFont: function(btn){
16763         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16764         //if(Roo.isSafari){ // safari
16765         //    adjust *= 2;
16766        // }
16767         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16768         if(Roo.isSafari){ // safari
16769             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16770             v =  (v < 10) ? 10 : v;
16771             v =  (v > 48) ? 48 : v;
16772             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16773             
16774         }
16775         
16776         
16777         v = Math.max(1, v+adjust);
16778         
16779         this.execCmd('FontSize', v  );
16780     },
16781
16782     onEditorEvent : function(e){
16783         this.owner.fireEvent('editorevent', this, e);
16784       //  this.updateToolbar();
16785         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16786     },
16787
16788     insertTag : function(tg)
16789     {
16790         // could be a bit smarter... -> wrap the current selected tRoo..
16791         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16792             
16793             range = this.createRange(this.getSelection());
16794             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16795             wrappingNode.appendChild(range.extractContents());
16796             range.insertNode(wrappingNode);
16797
16798             return;
16799             
16800             
16801             
16802         }
16803         this.execCmd("formatblock",   tg);
16804         
16805     },
16806     
16807     insertText : function(txt)
16808     {
16809         
16810         
16811         var range = this.createRange();
16812         range.deleteContents();
16813                //alert(Sender.getAttribute('label'));
16814                
16815         range.insertNode(this.doc.createTextNode(txt));
16816     } ,
16817     
16818      
16819
16820     /**
16821      * Executes a Midas editor command on the editor document and performs necessary focus and
16822      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16823      * @param {String} cmd The Midas command
16824      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16825      */
16826     relayCmd : function(cmd, value){
16827         this.win.focus();
16828         this.execCmd(cmd, value);
16829         this.owner.fireEvent('editorevent', this);
16830         //this.updateToolbar();
16831         this.owner.deferFocus();
16832     },
16833
16834     /**
16835      * Executes a Midas editor command directly on the editor document.
16836      * For visual commands, you should use {@link #relayCmd} instead.
16837      * <b>This should only be called after the editor is initialized.</b>
16838      * @param {String} cmd The Midas command
16839      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16840      */
16841     execCmd : function(cmd, value){
16842         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16843         this.syncValue();
16844     },
16845  
16846  
16847    
16848     /**
16849      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16850      * to insert tRoo.
16851      * @param {String} text | dom node.. 
16852      */
16853     insertAtCursor : function(text)
16854     {
16855         
16856         
16857         
16858         if(!this.activated){
16859             return;
16860         }
16861         /*
16862         if(Roo.isIE){
16863             this.win.focus();
16864             var r = this.doc.selection.createRange();
16865             if(r){
16866                 r.collapse(true);
16867                 r.pasteHTML(text);
16868                 this.syncValue();
16869                 this.deferFocus();
16870             
16871             }
16872             return;
16873         }
16874         */
16875         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16876             this.win.focus();
16877             
16878             
16879             // from jquery ui (MIT licenced)
16880             var range, node;
16881             var win = this.win;
16882             
16883             if (win.getSelection && win.getSelection().getRangeAt) {
16884                 range = win.getSelection().getRangeAt(0);
16885                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16886                 range.insertNode(node);
16887             } else if (win.document.selection && win.document.selection.createRange) {
16888                 // no firefox support
16889                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16890                 win.document.selection.createRange().pasteHTML(txt);
16891             } else {
16892                 // no firefox support
16893                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16894                 this.execCmd('InsertHTML', txt);
16895             } 
16896             
16897             this.syncValue();
16898             
16899             this.deferFocus();
16900         }
16901     },
16902  // private
16903     mozKeyPress : function(e){
16904         if(e.ctrlKey){
16905             var c = e.getCharCode(), cmd;
16906           
16907             if(c > 0){
16908                 c = String.fromCharCode(c).toLowerCase();
16909                 switch(c){
16910                     case 'b':
16911                         cmd = 'bold';
16912                         break;
16913                     case 'i':
16914                         cmd = 'italic';
16915                         break;
16916                     
16917                     case 'u':
16918                         cmd = 'underline';
16919                         break;
16920                     
16921                     case 'v':
16922                         this.cleanUpPaste.defer(100, this);
16923                         return;
16924                         
16925                 }
16926                 if(cmd){
16927                     this.win.focus();
16928                     this.execCmd(cmd);
16929                     this.deferFocus();
16930                     e.preventDefault();
16931                 }
16932                 
16933             }
16934         }
16935     },
16936
16937     // private
16938     fixKeys : function(){ // load time branching for fastest keydown performance
16939         if(Roo.isIE){
16940             return function(e){
16941                 var k = e.getKey(), r;
16942                 if(k == e.TAB){
16943                     e.stopEvent();
16944                     r = this.doc.selection.createRange();
16945                     if(r){
16946                         r.collapse(true);
16947                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16948                         this.deferFocus();
16949                     }
16950                     return;
16951                 }
16952                 
16953                 if(k == e.ENTER){
16954                     r = this.doc.selection.createRange();
16955                     if(r){
16956                         var target = r.parentElement();
16957                         if(!target || target.tagName.toLowerCase() != 'li'){
16958                             e.stopEvent();
16959                             r.pasteHTML('<br />');
16960                             r.collapse(false);
16961                             r.select();
16962                         }
16963                     }
16964                 }
16965                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16966                     this.cleanUpPaste.defer(100, this);
16967                     return;
16968                 }
16969                 
16970                 
16971             };
16972         }else if(Roo.isOpera){
16973             return function(e){
16974                 var k = e.getKey();
16975                 if(k == e.TAB){
16976                     e.stopEvent();
16977                     this.win.focus();
16978                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16979                     this.deferFocus();
16980                 }
16981                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16982                     this.cleanUpPaste.defer(100, this);
16983                     return;
16984                 }
16985                 
16986             };
16987         }else if(Roo.isSafari){
16988             return function(e){
16989                 var k = e.getKey();
16990                 
16991                 if(k == e.TAB){
16992                     e.stopEvent();
16993                     this.execCmd('InsertText','\t');
16994                     this.deferFocus();
16995                     return;
16996                 }
16997                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16998                     this.cleanUpPaste.defer(100, this);
16999                     return;
17000                 }
17001                 
17002              };
17003         }
17004     }(),
17005     
17006     getAllAncestors: function()
17007     {
17008         var p = this.getSelectedNode();
17009         var a = [];
17010         if (!p) {
17011             a.push(p); // push blank onto stack..
17012             p = this.getParentElement();
17013         }
17014         
17015         
17016         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17017             a.push(p);
17018             p = p.parentNode;
17019         }
17020         a.push(this.doc.body);
17021         return a;
17022     },
17023     lastSel : false,
17024     lastSelNode : false,
17025     
17026     
17027     getSelection : function() 
17028     {
17029         this.assignDocWin();
17030         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17031     },
17032     
17033     getSelectedNode: function() 
17034     {
17035         // this may only work on Gecko!!!
17036         
17037         // should we cache this!!!!
17038         
17039         
17040         
17041          
17042         var range = this.createRange(this.getSelection()).cloneRange();
17043         
17044         if (Roo.isIE) {
17045             var parent = range.parentElement();
17046             while (true) {
17047                 var testRange = range.duplicate();
17048                 testRange.moveToElementText(parent);
17049                 if (testRange.inRange(range)) {
17050                     break;
17051                 }
17052                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17053                     break;
17054                 }
17055                 parent = parent.parentElement;
17056             }
17057             return parent;
17058         }
17059         
17060         // is ancestor a text element.
17061         var ac =  range.commonAncestorContainer;
17062         if (ac.nodeType == 3) {
17063             ac = ac.parentNode;
17064         }
17065         
17066         var ar = ac.childNodes;
17067          
17068         var nodes = [];
17069         var other_nodes = [];
17070         var has_other_nodes = false;
17071         for (var i=0;i<ar.length;i++) {
17072             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17073                 continue;
17074             }
17075             // fullly contained node.
17076             
17077             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17078                 nodes.push(ar[i]);
17079                 continue;
17080             }
17081             
17082             // probably selected..
17083             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17084                 other_nodes.push(ar[i]);
17085                 continue;
17086             }
17087             // outer..
17088             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17089                 continue;
17090             }
17091             
17092             
17093             has_other_nodes = true;
17094         }
17095         if (!nodes.length && other_nodes.length) {
17096             nodes= other_nodes;
17097         }
17098         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17099             return false;
17100         }
17101         
17102         return nodes[0];
17103     },
17104     createRange: function(sel)
17105     {
17106         // this has strange effects when using with 
17107         // top toolbar - not sure if it's a great idea.
17108         //this.editor.contentWindow.focus();
17109         if (typeof sel != "undefined") {
17110             try {
17111                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17112             } catch(e) {
17113                 return this.doc.createRange();
17114             }
17115         } else {
17116             return this.doc.createRange();
17117         }
17118     },
17119     getParentElement: function()
17120     {
17121         
17122         this.assignDocWin();
17123         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17124         
17125         var range = this.createRange(sel);
17126          
17127         try {
17128             var p = range.commonAncestorContainer;
17129             while (p.nodeType == 3) { // text node
17130                 p = p.parentNode;
17131             }
17132             return p;
17133         } catch (e) {
17134             return null;
17135         }
17136     
17137     },
17138     /***
17139      *
17140      * Range intersection.. the hard stuff...
17141      *  '-1' = before
17142      *  '0' = hits..
17143      *  '1' = after.
17144      *         [ -- selected range --- ]
17145      *   [fail]                        [fail]
17146      *
17147      *    basically..
17148      *      if end is before start or  hits it. fail.
17149      *      if start is after end or hits it fail.
17150      *
17151      *   if either hits (but other is outside. - then it's not 
17152      *   
17153      *    
17154      **/
17155     
17156     
17157     // @see http://www.thismuchiknow.co.uk/?p=64.
17158     rangeIntersectsNode : function(range, node)
17159     {
17160         var nodeRange = node.ownerDocument.createRange();
17161         try {
17162             nodeRange.selectNode(node);
17163         } catch (e) {
17164             nodeRange.selectNodeContents(node);
17165         }
17166     
17167         var rangeStartRange = range.cloneRange();
17168         rangeStartRange.collapse(true);
17169     
17170         var rangeEndRange = range.cloneRange();
17171         rangeEndRange.collapse(false);
17172     
17173         var nodeStartRange = nodeRange.cloneRange();
17174         nodeStartRange.collapse(true);
17175     
17176         var nodeEndRange = nodeRange.cloneRange();
17177         nodeEndRange.collapse(false);
17178     
17179         return rangeStartRange.compareBoundaryPoints(
17180                  Range.START_TO_START, nodeEndRange) == -1 &&
17181                rangeEndRange.compareBoundaryPoints(
17182                  Range.START_TO_START, nodeStartRange) == 1;
17183         
17184          
17185     },
17186     rangeCompareNode : function(range, node)
17187     {
17188         var nodeRange = node.ownerDocument.createRange();
17189         try {
17190             nodeRange.selectNode(node);
17191         } catch (e) {
17192             nodeRange.selectNodeContents(node);
17193         }
17194         
17195         
17196         range.collapse(true);
17197     
17198         nodeRange.collapse(true);
17199      
17200         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17201         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17202          
17203         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17204         
17205         var nodeIsBefore   =  ss == 1;
17206         var nodeIsAfter    = ee == -1;
17207         
17208         if (nodeIsBefore && nodeIsAfter)
17209             return 0; // outer
17210         if (!nodeIsBefore && nodeIsAfter)
17211             return 1; //right trailed.
17212         
17213         if (nodeIsBefore && !nodeIsAfter)
17214             return 2;  // left trailed.
17215         // fully contined.
17216         return 3;
17217     },
17218
17219     // private? - in a new class?
17220     cleanUpPaste :  function()
17221     {
17222         // cleans up the whole document..
17223         Roo.log('cleanuppaste');
17224         
17225         this.cleanUpChildren(this.doc.body);
17226         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17227         if (clean != this.doc.body.innerHTML) {
17228             this.doc.body.innerHTML = clean;
17229         }
17230         
17231     },
17232     
17233     cleanWordChars : function(input) {// change the chars to hex code
17234         var he = Roo.HtmlEditorCore;
17235         
17236         var output = input;
17237         Roo.each(he.swapCodes, function(sw) { 
17238             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17239             
17240             output = output.replace(swapper, sw[1]);
17241         });
17242         
17243         return output;
17244     },
17245     
17246     
17247     cleanUpChildren : function (n)
17248     {
17249         if (!n.childNodes.length) {
17250             return;
17251         }
17252         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17253            this.cleanUpChild(n.childNodes[i]);
17254         }
17255     },
17256     
17257     
17258         
17259     
17260     cleanUpChild : function (node)
17261     {
17262         var ed = this;
17263         //console.log(node);
17264         if (node.nodeName == "#text") {
17265             // clean up silly Windows -- stuff?
17266             return; 
17267         }
17268         if (node.nodeName == "#comment") {
17269             node.parentNode.removeChild(node);
17270             // clean up silly Windows -- stuff?
17271             return; 
17272         }
17273         
17274         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17275             // remove node.
17276             node.parentNode.removeChild(node);
17277             return;
17278             
17279         }
17280         
17281         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17282         
17283         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17284         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17285         
17286         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17287         //    remove_keep_children = true;
17288         //}
17289         
17290         if (remove_keep_children) {
17291             this.cleanUpChildren(node);
17292             // inserts everything just before this node...
17293             while (node.childNodes.length) {
17294                 var cn = node.childNodes[0];
17295                 node.removeChild(cn);
17296                 node.parentNode.insertBefore(cn, node);
17297             }
17298             node.parentNode.removeChild(node);
17299             return;
17300         }
17301         
17302         if (!node.attributes || !node.attributes.length) {
17303             this.cleanUpChildren(node);
17304             return;
17305         }
17306         
17307         function cleanAttr(n,v)
17308         {
17309             
17310             if (v.match(/^\./) || v.match(/^\//)) {
17311                 return;
17312             }
17313             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17314                 return;
17315             }
17316             if (v.match(/^#/)) {
17317                 return;
17318             }
17319 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17320             node.removeAttribute(n);
17321             
17322         }
17323         
17324         function cleanStyle(n,v)
17325         {
17326             if (v.match(/expression/)) { //XSS?? should we even bother..
17327                 node.removeAttribute(n);
17328                 return;
17329             }
17330             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17331             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17332             
17333             
17334             var parts = v.split(/;/);
17335             var clean = [];
17336             
17337             Roo.each(parts, function(p) {
17338                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17339                 if (!p.length) {
17340                     return true;
17341                 }
17342                 var l = p.split(':').shift().replace(/\s+/g,'');
17343                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17344                 
17345                 if ( cblack.indexOf(l) > -1) {
17346 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17347                     //node.removeAttribute(n);
17348                     return true;
17349                 }
17350                 //Roo.log()
17351                 // only allow 'c whitelisted system attributes'
17352                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17353 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17354                     //node.removeAttribute(n);
17355                     return true;
17356                 }
17357                 
17358                 
17359                  
17360                 
17361                 clean.push(p);
17362                 return true;
17363             });
17364             if (clean.length) { 
17365                 node.setAttribute(n, clean.join(';'));
17366             } else {
17367                 node.removeAttribute(n);
17368             }
17369             
17370         }
17371         
17372         
17373         for (var i = node.attributes.length-1; i > -1 ; i--) {
17374             var a = node.attributes[i];
17375             //console.log(a);
17376             
17377             if (a.name.toLowerCase().substr(0,2)=='on')  {
17378                 node.removeAttribute(a.name);
17379                 continue;
17380             }
17381             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17382                 node.removeAttribute(a.name);
17383                 continue;
17384             }
17385             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17386                 cleanAttr(a.name,a.value); // fixme..
17387                 continue;
17388             }
17389             if (a.name == 'style') {
17390                 cleanStyle(a.name,a.value);
17391                 continue;
17392             }
17393             /// clean up MS crap..
17394             // tecnically this should be a list of valid class'es..
17395             
17396             
17397             if (a.name == 'class') {
17398                 if (a.value.match(/^Mso/)) {
17399                     node.className = '';
17400                 }
17401                 
17402                 if (a.value.match(/body/)) {
17403                     node.className = '';
17404                 }
17405                 continue;
17406             }
17407             
17408             // style cleanup!?
17409             // class cleanup?
17410             
17411         }
17412         
17413         
17414         this.cleanUpChildren(node);
17415         
17416         
17417     },
17418     /**
17419      * Clean up MS wordisms...
17420      */
17421     cleanWord : function(node)
17422     {
17423         var _t = this;
17424         var cleanWordChildren = function()
17425         {
17426             if (!node.childNodes.length) {
17427                 return;
17428             }
17429             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17430                _t.cleanWord(node.childNodes[i]);
17431             }
17432         }
17433         
17434         
17435         if (!node) {
17436             this.cleanWord(this.doc.body);
17437             return;
17438         }
17439         if (node.nodeName == "#text") {
17440             // clean up silly Windows -- stuff?
17441             return; 
17442         }
17443         if (node.nodeName == "#comment") {
17444             node.parentNode.removeChild(node);
17445             // clean up silly Windows -- stuff?
17446             return; 
17447         }
17448         
17449         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17450             node.parentNode.removeChild(node);
17451             return;
17452         }
17453         
17454         // remove - but keep children..
17455         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17456             while (node.childNodes.length) {
17457                 var cn = node.childNodes[0];
17458                 node.removeChild(cn);
17459                 node.parentNode.insertBefore(cn, node);
17460             }
17461             node.parentNode.removeChild(node);
17462             cleanWordChildren();
17463             return;
17464         }
17465         // clean styles
17466         if (node.className.length) {
17467             
17468             var cn = node.className.split(/\W+/);
17469             var cna = [];
17470             Roo.each(cn, function(cls) {
17471                 if (cls.match(/Mso[a-zA-Z]+/)) {
17472                     return;
17473                 }
17474                 cna.push(cls);
17475             });
17476             node.className = cna.length ? cna.join(' ') : '';
17477             if (!cna.length) {
17478                 node.removeAttribute("class");
17479             }
17480         }
17481         
17482         if (node.hasAttribute("lang")) {
17483             node.removeAttribute("lang");
17484         }
17485         
17486         if (node.hasAttribute("style")) {
17487             
17488             var styles = node.getAttribute("style").split(";");
17489             var nstyle = [];
17490             Roo.each(styles, function(s) {
17491                 if (!s.match(/:/)) {
17492                     return;
17493                 }
17494                 var kv = s.split(":");
17495                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17496                     return;
17497                 }
17498                 // what ever is left... we allow.
17499                 nstyle.push(s);
17500             });
17501             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17502             if (!nstyle.length) {
17503                 node.removeAttribute('style');
17504             }
17505         }
17506         
17507         cleanWordChildren();
17508         
17509         
17510     },
17511     domToHTML : function(currentElement, depth, nopadtext) {
17512         
17513             depth = depth || 0;
17514             nopadtext = nopadtext || false;
17515         
17516             if (!currentElement) {
17517                 return this.domToHTML(this.doc.body);
17518             }
17519             
17520             //Roo.log(currentElement);
17521             var j;
17522             var allText = false;
17523             var nodeName = currentElement.nodeName;
17524             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17525             
17526             if  (nodeName == '#text') {
17527                 return currentElement.nodeValue;
17528             }
17529             
17530             
17531             var ret = '';
17532             if (nodeName != 'BODY') {
17533                  
17534                 var i = 0;
17535                 // Prints the node tagName, such as <A>, <IMG>, etc
17536                 if (tagName) {
17537                     var attr = [];
17538                     for(i = 0; i < currentElement.attributes.length;i++) {
17539                         // quoting?
17540                         var aname = currentElement.attributes.item(i).name;
17541                         if (!currentElement.attributes.item(i).value.length) {
17542                             continue;
17543                         }
17544                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17545                     }
17546                     
17547                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17548                 } 
17549                 else {
17550                     
17551                     // eack
17552                 }
17553             } else {
17554                 tagName = false;
17555             }
17556             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17557                 return ret;
17558             }
17559             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17560                 nopadtext = true;
17561             }
17562             
17563             
17564             // Traverse the tree
17565             i = 0;
17566             var currentElementChild = currentElement.childNodes.item(i);
17567             var allText = true;
17568             var innerHTML  = '';
17569             lastnode = '';
17570             while (currentElementChild) {
17571                 // Formatting code (indent the tree so it looks nice on the screen)
17572                 var nopad = nopadtext;
17573                 if (lastnode == 'SPAN') {
17574                     nopad  = true;
17575                 }
17576                 // text
17577                 if  (currentElementChild.nodeName == '#text') {
17578                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17579                     if (!nopad && toadd.length > 80) {
17580                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17581                     }
17582                     innerHTML  += toadd;
17583                     
17584                     i++;
17585                     currentElementChild = currentElement.childNodes.item(i);
17586                     lastNode = '';
17587                     continue;
17588                 }
17589                 allText = false;
17590                 
17591                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17592                     
17593                 // Recursively traverse the tree structure of the child node
17594                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17595                 lastnode = currentElementChild.nodeName;
17596                 i++;
17597                 currentElementChild=currentElement.childNodes.item(i);
17598             }
17599             
17600             ret += innerHTML;
17601             
17602             if (!allText) {
17603                     // The remaining code is mostly for formatting the tree
17604                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17605             }
17606             
17607             
17608             if (tagName) {
17609                 ret+= "</"+tagName+">";
17610             }
17611             return ret;
17612             
17613         }
17614     
17615     // hide stuff that is not compatible
17616     /**
17617      * @event blur
17618      * @hide
17619      */
17620     /**
17621      * @event change
17622      * @hide
17623      */
17624     /**
17625      * @event focus
17626      * @hide
17627      */
17628     /**
17629      * @event specialkey
17630      * @hide
17631      */
17632     /**
17633      * @cfg {String} fieldClass @hide
17634      */
17635     /**
17636      * @cfg {String} focusClass @hide
17637      */
17638     /**
17639      * @cfg {String} autoCreate @hide
17640      */
17641     /**
17642      * @cfg {String} inputType @hide
17643      */
17644     /**
17645      * @cfg {String} invalidClass @hide
17646      */
17647     /**
17648      * @cfg {String} invalidText @hide
17649      */
17650     /**
17651      * @cfg {String} msgFx @hide
17652      */
17653     /**
17654      * @cfg {String} validateOnBlur @hide
17655      */
17656 });
17657
17658 Roo.HtmlEditorCore.white = [
17659         'area', 'br', 'img', 'input', 'hr', 'wbr',
17660         
17661        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17662        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17663        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17664        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17665        'table',   'ul',         'xmp', 
17666        
17667        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17668       'thead',   'tr', 
17669      
17670       'dir', 'menu', 'ol', 'ul', 'dl',
17671        
17672       'embed',  'object'
17673 ];
17674
17675
17676 Roo.HtmlEditorCore.black = [
17677     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17678         'applet', // 
17679         'base',   'basefont', 'bgsound', 'blink',  'body', 
17680         'frame',  'frameset', 'head',    'html',   'ilayer', 
17681         'iframe', 'layer',  'link',     'meta',    'object',   
17682         'script', 'style' ,'title',  'xml' // clean later..
17683 ];
17684 Roo.HtmlEditorCore.clean = [
17685     'script', 'style', 'title', 'xml'
17686 ];
17687 Roo.HtmlEditorCore.remove = [
17688     'font'
17689 ];
17690 // attributes..
17691
17692 Roo.HtmlEditorCore.ablack = [
17693     'on'
17694 ];
17695     
17696 Roo.HtmlEditorCore.aclean = [ 
17697     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17698 ];
17699
17700 // protocols..
17701 Roo.HtmlEditorCore.pwhite= [
17702         'http',  'https',  'mailto'
17703 ];
17704
17705 // white listed style attributes.
17706 Roo.HtmlEditorCore.cwhite= [
17707       //  'text-align', /// default is to allow most things..
17708       
17709          
17710 //        'font-size'//??
17711 ];
17712
17713 // black listed style attributes.
17714 Roo.HtmlEditorCore.cblack= [
17715       //  'font-size' -- this can be set by the project 
17716 ];
17717
17718
17719 Roo.HtmlEditorCore.swapCodes   =[ 
17720     [    8211, "--" ], 
17721     [    8212, "--" ], 
17722     [    8216,  "'" ],  
17723     [    8217, "'" ],  
17724     [    8220, '"' ],  
17725     [    8221, '"' ],  
17726     [    8226, "*" ],  
17727     [    8230, "..." ]
17728 ]; 
17729
17730     /*
17731  * - LGPL
17732  *
17733  * HtmlEditor
17734  * 
17735  */
17736
17737 /**
17738  * @class Roo.bootstrap.HtmlEditor
17739  * @extends Roo.bootstrap.TextArea
17740  * Bootstrap HtmlEditor class
17741
17742  * @constructor
17743  * Create a new HtmlEditor
17744  * @param {Object} config The config object
17745  */
17746
17747 Roo.bootstrap.HtmlEditor = function(config){
17748     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17749     if (!this.toolbars) {
17750         this.toolbars = [];
17751     }
17752     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17753     this.addEvents({
17754             /**
17755              * @event initialize
17756              * Fires when the editor is fully initialized (including the iframe)
17757              * @param {HtmlEditor} this
17758              */
17759             initialize: true,
17760             /**
17761              * @event activate
17762              * Fires when the editor is first receives the focus. Any insertion must wait
17763              * until after this event.
17764              * @param {HtmlEditor} this
17765              */
17766             activate: true,
17767              /**
17768              * @event beforesync
17769              * Fires before the textarea is updated with content from the editor iframe. Return false
17770              * to cancel the sync.
17771              * @param {HtmlEditor} this
17772              * @param {String} html
17773              */
17774             beforesync: true,
17775              /**
17776              * @event beforepush
17777              * Fires before the iframe editor is updated with content from the textarea. Return false
17778              * to cancel the push.
17779              * @param {HtmlEditor} this
17780              * @param {String} html
17781              */
17782             beforepush: true,
17783              /**
17784              * @event sync
17785              * Fires when the textarea is updated with content from the editor iframe.
17786              * @param {HtmlEditor} this
17787              * @param {String} html
17788              */
17789             sync: true,
17790              /**
17791              * @event push
17792              * Fires when the iframe editor is updated with content from the textarea.
17793              * @param {HtmlEditor} this
17794              * @param {String} html
17795              */
17796             push: true,
17797              /**
17798              * @event editmodechange
17799              * Fires when the editor switches edit modes
17800              * @param {HtmlEditor} this
17801              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17802              */
17803             editmodechange: true,
17804             /**
17805              * @event editorevent
17806              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17807              * @param {HtmlEditor} this
17808              */
17809             editorevent: true,
17810             /**
17811              * @event firstfocus
17812              * Fires when on first focus - needed by toolbars..
17813              * @param {HtmlEditor} this
17814              */
17815             firstfocus: true,
17816             /**
17817              * @event autosave
17818              * Auto save the htmlEditor value as a file into Events
17819              * @param {HtmlEditor} this
17820              */
17821             autosave: true,
17822             /**
17823              * @event savedpreview
17824              * preview the saved version of htmlEditor
17825              * @param {HtmlEditor} this
17826              */
17827             savedpreview: true
17828         });
17829 };
17830
17831
17832 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17833     
17834     
17835       /**
17836      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17837      */
17838     toolbars : false,
17839    
17840      /**
17841      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17842      *                        Roo.resizable.
17843      */
17844     resizable : false,
17845      /**
17846      * @cfg {Number} height (in pixels)
17847      */   
17848     height: 300,
17849    /**
17850      * @cfg {Number} width (in pixels)
17851      */   
17852     width: false,
17853     
17854     /**
17855      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17856      * 
17857      */
17858     stylesheets: false,
17859     
17860     // id of frame..
17861     frameId: false,
17862     
17863     // private properties
17864     validationEvent : false,
17865     deferHeight: true,
17866     initialized : false,
17867     activated : false,
17868     
17869     onFocus : Roo.emptyFn,
17870     iframePad:3,
17871     hideMode:'offsets',
17872     
17873     
17874     tbContainer : false,
17875     
17876     toolbarContainer :function() {
17877         return this.wrap.select('.x-html-editor-tb',true).first();
17878     },
17879
17880     /**
17881      * Protected method that will not generally be called directly. It
17882      * is called when the editor creates its toolbar. Override this method if you need to
17883      * add custom toolbar buttons.
17884      * @param {HtmlEditor} editor
17885      */
17886     createToolbar : function(){
17887         
17888         Roo.log("create toolbars");
17889         
17890         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17891         this.toolbars[0].render(this.toolbarContainer());
17892         
17893         return;
17894         
17895 //        if (!editor.toolbars || !editor.toolbars.length) {
17896 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17897 //        }
17898 //        
17899 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17900 //            editor.toolbars[i] = Roo.factory(
17901 //                    typeof(editor.toolbars[i]) == 'string' ?
17902 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17903 //                Roo.bootstrap.HtmlEditor);
17904 //            editor.toolbars[i].init(editor);
17905 //        }
17906     },
17907
17908      
17909     // private
17910     onRender : function(ct, position)
17911     {
17912        // Roo.log("Call onRender: " + this.xtype);
17913         var _t = this;
17914         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17915       
17916         this.wrap = this.inputEl().wrap({
17917             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17918         });
17919         
17920         this.editorcore.onRender(ct, position);
17921          
17922         if (this.resizable) {
17923             this.resizeEl = new Roo.Resizable(this.wrap, {
17924                 pinned : true,
17925                 wrap: true,
17926                 dynamic : true,
17927                 minHeight : this.height,
17928                 height: this.height,
17929                 handles : this.resizable,
17930                 width: this.width,
17931                 listeners : {
17932                     resize : function(r, w, h) {
17933                         _t.onResize(w,h); // -something
17934                     }
17935                 }
17936             });
17937             
17938         }
17939         this.createToolbar(this);
17940        
17941         
17942         if(!this.width && this.resizable){
17943             this.setSize(this.wrap.getSize());
17944         }
17945         if (this.resizeEl) {
17946             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17947             // should trigger onReize..
17948         }
17949         
17950     },
17951
17952     // private
17953     onResize : function(w, h)
17954     {
17955         Roo.log('resize: ' +w + ',' + h );
17956         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17957         var ew = false;
17958         var eh = false;
17959         
17960         if(this.inputEl() ){
17961             if(typeof w == 'number'){
17962                 var aw = w - this.wrap.getFrameWidth('lr');
17963                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17964                 ew = aw;
17965             }
17966             if(typeof h == 'number'){
17967                  var tbh = -11;  // fixme it needs to tool bar size!
17968                 for (var i =0; i < this.toolbars.length;i++) {
17969                     // fixme - ask toolbars for heights?
17970                     tbh += this.toolbars[i].el.getHeight();
17971                     //if (this.toolbars[i].footer) {
17972                     //    tbh += this.toolbars[i].footer.el.getHeight();
17973                     //}
17974                 }
17975               
17976                 
17977                 
17978                 
17979                 
17980                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17981                 ah -= 5; // knock a few pixes off for look..
17982                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17983                 var eh = ah;
17984             }
17985         }
17986         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17987         this.editorcore.onResize(ew,eh);
17988         
17989     },
17990
17991     /**
17992      * Toggles the editor between standard and source edit mode.
17993      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17994      */
17995     toggleSourceEdit : function(sourceEditMode)
17996     {
17997         this.editorcore.toggleSourceEdit(sourceEditMode);
17998         
17999         if(this.editorcore.sourceEditMode){
18000             Roo.log('editor - showing textarea');
18001             
18002 //            Roo.log('in');
18003 //            Roo.log(this.syncValue());
18004             this.syncValue();
18005             this.inputEl().removeClass(['hide', 'x-hidden']);
18006             this.inputEl().dom.removeAttribute('tabIndex');
18007             this.inputEl().focus();
18008         }else{
18009             Roo.log('editor - hiding textarea');
18010 //            Roo.log('out')
18011 //            Roo.log(this.pushValue()); 
18012             this.pushValue();
18013             
18014             this.inputEl().addClass(['hide', 'x-hidden']);
18015             this.inputEl().dom.setAttribute('tabIndex', -1);
18016             //this.deferFocus();
18017         }
18018          
18019         if(this.resizable){
18020             this.setSize(this.wrap.getSize());
18021         }
18022         
18023         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18024     },
18025  
18026     // private (for BoxComponent)
18027     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18028
18029     // private (for BoxComponent)
18030     getResizeEl : function(){
18031         return this.wrap;
18032     },
18033
18034     // private (for BoxComponent)
18035     getPositionEl : function(){
18036         return this.wrap;
18037     },
18038
18039     // private
18040     initEvents : function(){
18041         this.originalValue = this.getValue();
18042     },
18043
18044 //    /**
18045 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18046 //     * @method
18047 //     */
18048 //    markInvalid : Roo.emptyFn,
18049 //    /**
18050 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18051 //     * @method
18052 //     */
18053 //    clearInvalid : Roo.emptyFn,
18054
18055     setValue : function(v){
18056         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18057         this.editorcore.pushValue();
18058     },
18059
18060      
18061     // private
18062     deferFocus : function(){
18063         this.focus.defer(10, this);
18064     },
18065
18066     // doc'ed in Field
18067     focus : function(){
18068         this.editorcore.focus();
18069         
18070     },
18071       
18072
18073     // private
18074     onDestroy : function(){
18075         
18076         
18077         
18078         if(this.rendered){
18079             
18080             for (var i =0; i < this.toolbars.length;i++) {
18081                 // fixme - ask toolbars for heights?
18082                 this.toolbars[i].onDestroy();
18083             }
18084             
18085             this.wrap.dom.innerHTML = '';
18086             this.wrap.remove();
18087         }
18088     },
18089
18090     // private
18091     onFirstFocus : function(){
18092         //Roo.log("onFirstFocus");
18093         this.editorcore.onFirstFocus();
18094          for (var i =0; i < this.toolbars.length;i++) {
18095             this.toolbars[i].onFirstFocus();
18096         }
18097         
18098     },
18099     
18100     // private
18101     syncValue : function()
18102     {   
18103         this.editorcore.syncValue();
18104     },
18105     
18106     pushValue : function()
18107     {   
18108         this.editorcore.pushValue();
18109     }
18110      
18111     
18112     // hide stuff that is not compatible
18113     /**
18114      * @event blur
18115      * @hide
18116      */
18117     /**
18118      * @event change
18119      * @hide
18120      */
18121     /**
18122      * @event focus
18123      * @hide
18124      */
18125     /**
18126      * @event specialkey
18127      * @hide
18128      */
18129     /**
18130      * @cfg {String} fieldClass @hide
18131      */
18132     /**
18133      * @cfg {String} focusClass @hide
18134      */
18135     /**
18136      * @cfg {String} autoCreate @hide
18137      */
18138     /**
18139      * @cfg {String} inputType @hide
18140      */
18141     /**
18142      * @cfg {String} invalidClass @hide
18143      */
18144     /**
18145      * @cfg {String} invalidText @hide
18146      */
18147     /**
18148      * @cfg {String} msgFx @hide
18149      */
18150     /**
18151      * @cfg {String} validateOnBlur @hide
18152      */
18153 });
18154  
18155     
18156    
18157    
18158    
18159       
18160 Roo.namespace('Roo.bootstrap.htmleditor');
18161 /**
18162  * @class Roo.bootstrap.HtmlEditorToolbar1
18163  * Basic Toolbar
18164  * 
18165  * Usage:
18166  *
18167  new Roo.bootstrap.HtmlEditor({
18168     ....
18169     toolbars : [
18170         new Roo.bootstrap.HtmlEditorToolbar1({
18171             disable : { fonts: 1 , format: 1, ..., ... , ...],
18172             btns : [ .... ]
18173         })
18174     }
18175      
18176  * 
18177  * @cfg {Object} disable List of elements to disable..
18178  * @cfg {Array} btns List of additional buttons.
18179  * 
18180  * 
18181  * NEEDS Extra CSS? 
18182  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18183  */
18184  
18185 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18186 {
18187     
18188     Roo.apply(this, config);
18189     
18190     // default disabled, based on 'good practice'..
18191     this.disable = this.disable || {};
18192     Roo.applyIf(this.disable, {
18193         fontSize : true,
18194         colors : true,
18195         specialElements : true
18196     });
18197     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18198     
18199     this.editor = config.editor;
18200     this.editorcore = config.editor.editorcore;
18201     
18202     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18203     
18204     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18205     // dont call parent... till later.
18206 }
18207 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18208      
18209     bar : true,
18210     
18211     editor : false,
18212     editorcore : false,
18213     
18214     
18215     formats : [
18216         "p" ,  
18217         "h1","h2","h3","h4","h5","h6", 
18218         "pre", "code", 
18219         "abbr", "acronym", "address", "cite", "samp", "var",
18220         'div','span'
18221     ],
18222     
18223     onRender : function(ct, position)
18224     {
18225        // Roo.log("Call onRender: " + this.xtype);
18226         
18227        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18228        Roo.log(this.el);
18229        this.el.dom.style.marginBottom = '0';
18230        var _this = this;
18231        var editorcore = this.editorcore;
18232        var editor= this.editor;
18233        
18234        var children = [];
18235        var btn = function(id,cmd , toggle, handler){
18236        
18237             var  event = toggle ? 'toggle' : 'click';
18238        
18239             var a = {
18240                 size : 'sm',
18241                 xtype: 'Button',
18242                 xns: Roo.bootstrap,
18243                 glyphicon : id,
18244                 cmd : id || cmd,
18245                 enableToggle:toggle !== false,
18246                 //html : 'submit'
18247                 pressed : toggle ? false : null,
18248                 listeners : {}
18249             }
18250             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18251                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18252             }
18253             children.push(a);
18254             return a;
18255        }
18256         
18257         var style = {
18258                 xtype: 'Button',
18259                 size : 'sm',
18260                 xns: Roo.bootstrap,
18261                 glyphicon : 'font',
18262                 //html : 'submit'
18263                 menu : {
18264                     xtype: 'Menu',
18265                     xns: Roo.bootstrap,
18266                     items:  []
18267                 }
18268         };
18269         Roo.each(this.formats, function(f) {
18270             style.menu.items.push({
18271                 xtype :'MenuItem',
18272                 xns: Roo.bootstrap,
18273                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18274                 tagname : f,
18275                 listeners : {
18276                     click : function()
18277                     {
18278                         editorcore.insertTag(this.tagname);
18279                         editor.focus();
18280                     }
18281                 }
18282                 
18283             });
18284         });
18285          children.push(style);   
18286             
18287             
18288         btn('bold',false,true);
18289         btn('italic',false,true);
18290         btn('align-left', 'justifyleft',true);
18291         btn('align-center', 'justifycenter',true);
18292         btn('align-right' , 'justifyright',true);
18293         btn('link', false, false, function(btn) {
18294             //Roo.log("create link?");
18295             var url = prompt(this.createLinkText, this.defaultLinkValue);
18296             if(url && url != 'http:/'+'/'){
18297                 this.editorcore.relayCmd('createlink', url);
18298             }
18299         }),
18300         btn('list','insertunorderedlist',true);
18301         btn('pencil', false,true, function(btn){
18302                 Roo.log(this);
18303                 
18304                 this.toggleSourceEdit(btn.pressed);
18305         });
18306         /*
18307         var cog = {
18308                 xtype: 'Button',
18309                 size : 'sm',
18310                 xns: Roo.bootstrap,
18311                 glyphicon : 'cog',
18312                 //html : 'submit'
18313                 menu : {
18314                     xtype: 'Menu',
18315                     xns: Roo.bootstrap,
18316                     items:  []
18317                 }
18318         };
18319         
18320         cog.menu.items.push({
18321             xtype :'MenuItem',
18322             xns: Roo.bootstrap,
18323             html : Clean styles,
18324             tagname : f,
18325             listeners : {
18326                 click : function()
18327                 {
18328                     editorcore.insertTag(this.tagname);
18329                     editor.focus();
18330                 }
18331             }
18332             
18333         });
18334        */
18335         
18336          
18337        this.xtype = 'NavSimplebar';
18338         
18339         for(var i=0;i< children.length;i++) {
18340             
18341             this.buttons.add(this.addxtypeChild(children[i]));
18342             
18343         }
18344         
18345         editor.on('editorevent', this.updateToolbar, this);
18346     },
18347     onBtnClick : function(id)
18348     {
18349        this.editorcore.relayCmd(id);
18350        this.editorcore.focus();
18351     },
18352     
18353     /**
18354      * Protected method that will not generally be called directly. It triggers
18355      * a toolbar update by reading the markup state of the current selection in the editor.
18356      */
18357     updateToolbar: function(){
18358
18359         if(!this.editorcore.activated){
18360             this.editor.onFirstFocus(); // is this neeed?
18361             return;
18362         }
18363
18364         var btns = this.buttons; 
18365         var doc = this.editorcore.doc;
18366         btns.get('bold').setActive(doc.queryCommandState('bold'));
18367         btns.get('italic').setActive(doc.queryCommandState('italic'));
18368         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18369         
18370         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18371         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18372         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18373         
18374         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18375         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18376          /*
18377         
18378         var ans = this.editorcore.getAllAncestors();
18379         if (this.formatCombo) {
18380             
18381             
18382             var store = this.formatCombo.store;
18383             this.formatCombo.setValue("");
18384             for (var i =0; i < ans.length;i++) {
18385                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18386                     // select it..
18387                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18388                     break;
18389                 }
18390             }
18391         }
18392         
18393         
18394         
18395         // hides menus... - so this cant be on a menu...
18396         Roo.bootstrap.MenuMgr.hideAll();
18397         */
18398         Roo.bootstrap.MenuMgr.hideAll();
18399         //this.editorsyncValue();
18400     },
18401     onFirstFocus: function() {
18402         this.buttons.each(function(item){
18403            item.enable();
18404         });
18405     },
18406     toggleSourceEdit : function(sourceEditMode){
18407         
18408           
18409         if(sourceEditMode){
18410             Roo.log("disabling buttons");
18411            this.buttons.each( function(item){
18412                 if(item.cmd != 'pencil'){
18413                     item.disable();
18414                 }
18415             });
18416           
18417         }else{
18418             Roo.log("enabling buttons");
18419             if(this.editorcore.initialized){
18420                 this.buttons.each( function(item){
18421                     item.enable();
18422                 });
18423             }
18424             
18425         }
18426         Roo.log("calling toggole on editor");
18427         // tell the editor that it's been pressed..
18428         this.editor.toggleSourceEdit(sourceEditMode);
18429        
18430     }
18431 });
18432
18433
18434
18435
18436
18437 /**
18438  * @class Roo.bootstrap.Table.AbstractSelectionModel
18439  * @extends Roo.util.Observable
18440  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18441  * implemented by descendant classes.  This class should not be directly instantiated.
18442  * @constructor
18443  */
18444 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18445     this.locked = false;
18446     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18447 };
18448
18449
18450 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18451     /** @ignore Called by the grid automatically. Do not call directly. */
18452     init : function(grid){
18453         this.grid = grid;
18454         this.initEvents();
18455     },
18456
18457     /**
18458      * Locks the selections.
18459      */
18460     lock : function(){
18461         this.locked = true;
18462     },
18463
18464     /**
18465      * Unlocks the selections.
18466      */
18467     unlock : function(){
18468         this.locked = false;
18469     },
18470
18471     /**
18472      * Returns true if the selections are locked.
18473      * @return {Boolean}
18474      */
18475     isLocked : function(){
18476         return this.locked;
18477     }
18478 });
18479 /**
18480  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18481  * @class Roo.bootstrap.Table.RowSelectionModel
18482  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18483  * It supports multiple selections and keyboard selection/navigation. 
18484  * @constructor
18485  * @param {Object} config
18486  */
18487
18488 Roo.bootstrap.Table.RowSelectionModel = function(config){
18489     Roo.apply(this, config);
18490     this.selections = new Roo.util.MixedCollection(false, function(o){
18491         return o.id;
18492     });
18493
18494     this.last = false;
18495     this.lastActive = false;
18496
18497     this.addEvents({
18498         /**
18499              * @event selectionchange
18500              * Fires when the selection changes
18501              * @param {SelectionModel} this
18502              */
18503             "selectionchange" : true,
18504         /**
18505              * @event afterselectionchange
18506              * Fires after the selection changes (eg. by key press or clicking)
18507              * @param {SelectionModel} this
18508              */
18509             "afterselectionchange" : true,
18510         /**
18511              * @event beforerowselect
18512              * Fires when a row is selected being selected, return false to cancel.
18513              * @param {SelectionModel} this
18514              * @param {Number} rowIndex The selected index
18515              * @param {Boolean} keepExisting False if other selections will be cleared
18516              */
18517             "beforerowselect" : true,
18518         /**
18519              * @event rowselect
18520              * Fires when a row is selected.
18521              * @param {SelectionModel} this
18522              * @param {Number} rowIndex The selected index
18523              * @param {Roo.data.Record} r The record
18524              */
18525             "rowselect" : true,
18526         /**
18527              * @event rowdeselect
18528              * Fires when a row is deselected.
18529              * @param {SelectionModel} this
18530              * @param {Number} rowIndex The selected index
18531              */
18532         "rowdeselect" : true
18533     });
18534     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18535     this.locked = false;
18536 };
18537
18538 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18539     /**
18540      * @cfg {Boolean} singleSelect
18541      * True to allow selection of only one row at a time (defaults to false)
18542      */
18543     singleSelect : false,
18544
18545     // private
18546     initEvents : function(){
18547
18548         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18549             this.grid.on("mousedown", this.handleMouseDown, this);
18550         }else{ // allow click to work like normal
18551             this.grid.on("rowclick", this.handleDragableRowClick, this);
18552         }
18553
18554         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18555             "up" : function(e){
18556                 if(!e.shiftKey){
18557                     this.selectPrevious(e.shiftKey);
18558                 }else if(this.last !== false && this.lastActive !== false){
18559                     var last = this.last;
18560                     this.selectRange(this.last,  this.lastActive-1);
18561                     this.grid.getView().focusRow(this.lastActive);
18562                     if(last !== false){
18563                         this.last = last;
18564                     }
18565                 }else{
18566                     this.selectFirstRow();
18567                 }
18568                 this.fireEvent("afterselectionchange", this);
18569             },
18570             "down" : function(e){
18571                 if(!e.shiftKey){
18572                     this.selectNext(e.shiftKey);
18573                 }else if(this.last !== false && this.lastActive !== false){
18574                     var last = this.last;
18575                     this.selectRange(this.last,  this.lastActive+1);
18576                     this.grid.getView().focusRow(this.lastActive);
18577                     if(last !== false){
18578                         this.last = last;
18579                     }
18580                 }else{
18581                     this.selectFirstRow();
18582                 }
18583                 this.fireEvent("afterselectionchange", this);
18584             },
18585             scope: this
18586         });
18587
18588         var view = this.grid.view;
18589         view.on("refresh", this.onRefresh, this);
18590         view.on("rowupdated", this.onRowUpdated, this);
18591         view.on("rowremoved", this.onRemove, this);
18592     },
18593
18594     // private
18595     onRefresh : function(){
18596         var ds = this.grid.dataSource, i, v = this.grid.view;
18597         var s = this.selections;
18598         s.each(function(r){
18599             if((i = ds.indexOfId(r.id)) != -1){
18600                 v.onRowSelect(i);
18601             }else{
18602                 s.remove(r);
18603             }
18604         });
18605     },
18606
18607     // private
18608     onRemove : function(v, index, r){
18609         this.selections.remove(r);
18610     },
18611
18612     // private
18613     onRowUpdated : function(v, index, r){
18614         if(this.isSelected(r)){
18615             v.onRowSelect(index);
18616         }
18617     },
18618
18619     /**
18620      * Select records.
18621      * @param {Array} records The records to select
18622      * @param {Boolean} keepExisting (optional) True to keep existing selections
18623      */
18624     selectRecords : function(records, keepExisting){
18625         if(!keepExisting){
18626             this.clearSelections();
18627         }
18628         var ds = this.grid.dataSource;
18629         for(var i = 0, len = records.length; i < len; i++){
18630             this.selectRow(ds.indexOf(records[i]), true);
18631         }
18632     },
18633
18634     /**
18635      * Gets the number of selected rows.
18636      * @return {Number}
18637      */
18638     getCount : function(){
18639         return this.selections.length;
18640     },
18641
18642     /**
18643      * Selects the first row in the grid.
18644      */
18645     selectFirstRow : function(){
18646         this.selectRow(0);
18647     },
18648
18649     /**
18650      * Select the last row.
18651      * @param {Boolean} keepExisting (optional) True to keep existing selections
18652      */
18653     selectLastRow : function(keepExisting){
18654         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18655     },
18656
18657     /**
18658      * Selects the row immediately following the last selected row.
18659      * @param {Boolean} keepExisting (optional) True to keep existing selections
18660      */
18661     selectNext : function(keepExisting){
18662         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18663             this.selectRow(this.last+1, keepExisting);
18664             this.grid.getView().focusRow(this.last);
18665         }
18666     },
18667
18668     /**
18669      * Selects the row that precedes the last selected row.
18670      * @param {Boolean} keepExisting (optional) True to keep existing selections
18671      */
18672     selectPrevious : function(keepExisting){
18673         if(this.last){
18674             this.selectRow(this.last-1, keepExisting);
18675             this.grid.getView().focusRow(this.last);
18676         }
18677     },
18678
18679     /**
18680      * Returns the selected records
18681      * @return {Array} Array of selected records
18682      */
18683     getSelections : function(){
18684         return [].concat(this.selections.items);
18685     },
18686
18687     /**
18688      * Returns the first selected record.
18689      * @return {Record}
18690      */
18691     getSelected : function(){
18692         return this.selections.itemAt(0);
18693     },
18694
18695
18696     /**
18697      * Clears all selections.
18698      */
18699     clearSelections : function(fast){
18700         if(this.locked) return;
18701         if(fast !== true){
18702             var ds = this.grid.dataSource;
18703             var s = this.selections;
18704             s.each(function(r){
18705                 this.deselectRow(ds.indexOfId(r.id));
18706             }, this);
18707             s.clear();
18708         }else{
18709             this.selections.clear();
18710         }
18711         this.last = false;
18712     },
18713
18714
18715     /**
18716      * Selects all rows.
18717      */
18718     selectAll : function(){
18719         if(this.locked) return;
18720         this.selections.clear();
18721         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18722             this.selectRow(i, true);
18723         }
18724     },
18725
18726     /**
18727      * Returns True if there is a selection.
18728      * @return {Boolean}
18729      */
18730     hasSelection : function(){
18731         return this.selections.length > 0;
18732     },
18733
18734     /**
18735      * Returns True if the specified row is selected.
18736      * @param {Number/Record} record The record or index of the record to check
18737      * @return {Boolean}
18738      */
18739     isSelected : function(index){
18740         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18741         return (r && this.selections.key(r.id) ? true : false);
18742     },
18743
18744     /**
18745      * Returns True if the specified record id is selected.
18746      * @param {String} id The id of record to check
18747      * @return {Boolean}
18748      */
18749     isIdSelected : function(id){
18750         return (this.selections.key(id) ? true : false);
18751     },
18752
18753     // private
18754     handleMouseDown : function(e, t){
18755         var view = this.grid.getView(), rowIndex;
18756         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18757             return;
18758         };
18759         if(e.shiftKey && this.last !== false){
18760             var last = this.last;
18761             this.selectRange(last, rowIndex, e.ctrlKey);
18762             this.last = last; // reset the last
18763             view.focusRow(rowIndex);
18764         }else{
18765             var isSelected = this.isSelected(rowIndex);
18766             if(e.button !== 0 && isSelected){
18767                 view.focusRow(rowIndex);
18768             }else if(e.ctrlKey && isSelected){
18769                 this.deselectRow(rowIndex);
18770             }else if(!isSelected){
18771                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18772                 view.focusRow(rowIndex);
18773             }
18774         }
18775         this.fireEvent("afterselectionchange", this);
18776     },
18777     // private
18778     handleDragableRowClick :  function(grid, rowIndex, e) 
18779     {
18780         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18781             this.selectRow(rowIndex, false);
18782             grid.view.focusRow(rowIndex);
18783              this.fireEvent("afterselectionchange", this);
18784         }
18785     },
18786     
18787     /**
18788      * Selects multiple rows.
18789      * @param {Array} rows Array of the indexes of the row to select
18790      * @param {Boolean} keepExisting (optional) True to keep existing selections
18791      */
18792     selectRows : function(rows, keepExisting){
18793         if(!keepExisting){
18794             this.clearSelections();
18795         }
18796         for(var i = 0, len = rows.length; i < len; i++){
18797             this.selectRow(rows[i], true);
18798         }
18799     },
18800
18801     /**
18802      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18803      * @param {Number} startRow The index of the first row in the range
18804      * @param {Number} endRow The index of the last row in the range
18805      * @param {Boolean} keepExisting (optional) True to retain existing selections
18806      */
18807     selectRange : function(startRow, endRow, keepExisting){
18808         if(this.locked) return;
18809         if(!keepExisting){
18810             this.clearSelections();
18811         }
18812         if(startRow <= endRow){
18813             for(var i = startRow; i <= endRow; i++){
18814                 this.selectRow(i, true);
18815             }
18816         }else{
18817             for(var i = startRow; i >= endRow; i--){
18818                 this.selectRow(i, true);
18819             }
18820         }
18821     },
18822
18823     /**
18824      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18825      * @param {Number} startRow The index of the first row in the range
18826      * @param {Number} endRow The index of the last row in the range
18827      */
18828     deselectRange : function(startRow, endRow, preventViewNotify){
18829         if(this.locked) return;
18830         for(var i = startRow; i <= endRow; i++){
18831             this.deselectRow(i, preventViewNotify);
18832         }
18833     },
18834
18835     /**
18836      * Selects a row.
18837      * @param {Number} row The index of the row to select
18838      * @param {Boolean} keepExisting (optional) True to keep existing selections
18839      */
18840     selectRow : function(index, keepExisting, preventViewNotify){
18841         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18842         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18843             if(!keepExisting || this.singleSelect){
18844                 this.clearSelections();
18845             }
18846             var r = this.grid.dataSource.getAt(index);
18847             this.selections.add(r);
18848             this.last = this.lastActive = index;
18849             if(!preventViewNotify){
18850                 this.grid.getView().onRowSelect(index);
18851             }
18852             this.fireEvent("rowselect", this, index, r);
18853             this.fireEvent("selectionchange", this);
18854         }
18855     },
18856
18857     /**
18858      * Deselects a row.
18859      * @param {Number} row The index of the row to deselect
18860      */
18861     deselectRow : function(index, preventViewNotify){
18862         if(this.locked) return;
18863         if(this.last == index){
18864             this.last = false;
18865         }
18866         if(this.lastActive == index){
18867             this.lastActive = false;
18868         }
18869         var r = this.grid.dataSource.getAt(index);
18870         this.selections.remove(r);
18871         if(!preventViewNotify){
18872             this.grid.getView().onRowDeselect(index);
18873         }
18874         this.fireEvent("rowdeselect", this, index);
18875         this.fireEvent("selectionchange", this);
18876     },
18877
18878     // private
18879     restoreLast : function(){
18880         if(this._last){
18881             this.last = this._last;
18882         }
18883     },
18884
18885     // private
18886     acceptsNav : function(row, col, cm){
18887         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18888     },
18889
18890     // private
18891     onEditorKey : function(field, e){
18892         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18893         if(k == e.TAB){
18894             e.stopEvent();
18895             ed.completeEdit();
18896             if(e.shiftKey){
18897                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18898             }else{
18899                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18900             }
18901         }else if(k == e.ENTER && !e.ctrlKey){
18902             e.stopEvent();
18903             ed.completeEdit();
18904             if(e.shiftKey){
18905                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18906             }else{
18907                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18908             }
18909         }else if(k == e.ESC){
18910             ed.cancelEdit();
18911         }
18912         if(newCell){
18913             g.startEditing(newCell[0], newCell[1]);
18914         }
18915     }
18916 });/*
18917  * Based on:
18918  * Ext JS Library 1.1.1
18919  * Copyright(c) 2006-2007, Ext JS, LLC.
18920  *
18921  * Originally Released Under LGPL - original licence link has changed is not relivant.
18922  *
18923  * Fork - LGPL
18924  * <script type="text/javascript">
18925  */
18926  
18927 /**
18928  * @class Roo.bootstrap.PagingToolbar
18929  * @extends Roo.Row
18930  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18931  * @constructor
18932  * Create a new PagingToolbar
18933  * @param {Object} config The config object
18934  */
18935 Roo.bootstrap.PagingToolbar = function(config)
18936 {
18937     // old args format still supported... - xtype is prefered..
18938         // created from xtype...
18939     var ds = config.dataSource;
18940     this.toolbarItems = [];
18941     if (config.items) {
18942         this.toolbarItems = config.items;
18943 //        config.items = [];
18944     }
18945     
18946     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18947     this.ds = ds;
18948     this.cursor = 0;
18949     if (ds) { 
18950         this.bind(ds);
18951     }
18952     
18953     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18954     
18955 };
18956
18957 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18958     /**
18959      * @cfg {Roo.data.Store} dataSource
18960      * The underlying data store providing the paged data
18961      */
18962     /**
18963      * @cfg {String/HTMLElement/Element} container
18964      * container The id or element that will contain the toolbar
18965      */
18966     /**
18967      * @cfg {Boolean} displayInfo
18968      * True to display the displayMsg (defaults to false)
18969      */
18970     /**
18971      * @cfg {Number} pageSize
18972      * The number of records to display per page (defaults to 20)
18973      */
18974     pageSize: 20,
18975     /**
18976      * @cfg {String} displayMsg
18977      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18978      */
18979     displayMsg : 'Displaying {0} - {1} of {2}',
18980     /**
18981      * @cfg {String} emptyMsg
18982      * The message to display when no records are found (defaults to "No data to display")
18983      */
18984     emptyMsg : 'No data to display',
18985     /**
18986      * Customizable piece of the default paging text (defaults to "Page")
18987      * @type String
18988      */
18989     beforePageText : "Page",
18990     /**
18991      * Customizable piece of the default paging text (defaults to "of %0")
18992      * @type String
18993      */
18994     afterPageText : "of {0}",
18995     /**
18996      * Customizable piece of the default paging text (defaults to "First Page")
18997      * @type String
18998      */
18999     firstText : "First Page",
19000     /**
19001      * Customizable piece of the default paging text (defaults to "Previous Page")
19002      * @type String
19003      */
19004     prevText : "Previous Page",
19005     /**
19006      * Customizable piece of the default paging text (defaults to "Next Page")
19007      * @type String
19008      */
19009     nextText : "Next Page",
19010     /**
19011      * Customizable piece of the default paging text (defaults to "Last Page")
19012      * @type String
19013      */
19014     lastText : "Last Page",
19015     /**
19016      * Customizable piece of the default paging text (defaults to "Refresh")
19017      * @type String
19018      */
19019     refreshText : "Refresh",
19020
19021     buttons : false,
19022     // private
19023     onRender : function(ct, position) 
19024     {
19025         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19026         this.navgroup.parentId = this.id;
19027         this.navgroup.onRender(this.el, null);
19028         // add the buttons to the navgroup
19029         
19030         if(this.displayInfo){
19031             Roo.log(this.el.select('ul.navbar-nav',true).first());
19032             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19033             this.displayEl = this.el.select('.x-paging-info', true).first();
19034 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19035 //            this.displayEl = navel.el.select('span',true).first();
19036         }
19037         
19038         var _this = this;
19039         
19040         if(this.buttons){
19041             Roo.each(_this.buttons, function(e){
19042                Roo.factory(e).onRender(_this.el, null);
19043             });
19044         }
19045             
19046         Roo.each(_this.toolbarItems, function(e) {
19047             _this.navgroup.addItem(e);
19048         });
19049         
19050         this.first = this.navgroup.addItem({
19051             tooltip: this.firstText,
19052             cls: "prev",
19053             icon : 'fa fa-backward',
19054             disabled: true,
19055             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19056         });
19057         
19058         this.prev =  this.navgroup.addItem({
19059             tooltip: this.prevText,
19060             cls: "prev",
19061             icon : 'fa fa-step-backward',
19062             disabled: true,
19063             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19064         });
19065     //this.addSeparator();
19066         
19067         
19068         var field = this.navgroup.addItem( {
19069             tagtype : 'span',
19070             cls : 'x-paging-position',
19071             
19072             html : this.beforePageText  +
19073                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19074                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19075          } ); //?? escaped?
19076         
19077         this.field = field.el.select('input', true).first();
19078         this.field.on("keydown", this.onPagingKeydown, this);
19079         this.field.on("focus", function(){this.dom.select();});
19080     
19081     
19082         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19083         //this.field.setHeight(18);
19084         //this.addSeparator();
19085         this.next = this.navgroup.addItem({
19086             tooltip: this.nextText,
19087             cls: "next",
19088             html : ' <i class="fa fa-step-forward">',
19089             disabled: true,
19090             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19091         });
19092         this.last = this.navgroup.addItem({
19093             tooltip: this.lastText,
19094             icon : 'fa fa-forward',
19095             cls: "next",
19096             disabled: true,
19097             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19098         });
19099     //this.addSeparator();
19100         this.loading = this.navgroup.addItem({
19101             tooltip: this.refreshText,
19102             icon: 'fa fa-refresh',
19103             
19104             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19105         });
19106
19107     },
19108
19109     // private
19110     updateInfo : function(){
19111         if(this.displayEl){
19112             var count = this.ds.getCount();
19113             var msg = count == 0 ?
19114                 this.emptyMsg :
19115                 String.format(
19116                     this.displayMsg,
19117                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19118                 );
19119             this.displayEl.update(msg);
19120         }
19121     },
19122
19123     // private
19124     onLoad : function(ds, r, o){
19125        this.cursor = o.params ? o.params.start : 0;
19126        var d = this.getPageData(),
19127             ap = d.activePage,
19128             ps = d.pages;
19129         
19130        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19131        this.field.dom.value = ap;
19132        this.first.setDisabled(ap == 1);
19133        this.prev.setDisabled(ap == 1);
19134        this.next.setDisabled(ap == ps);
19135        this.last.setDisabled(ap == ps);
19136        this.loading.enable();
19137        this.updateInfo();
19138     },
19139
19140     // private
19141     getPageData : function(){
19142         var total = this.ds.getTotalCount();
19143         return {
19144             total : total,
19145             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19146             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19147         };
19148     },
19149
19150     // private
19151     onLoadError : function(){
19152         this.loading.enable();
19153     },
19154
19155     // private
19156     onPagingKeydown : function(e){
19157         var k = e.getKey();
19158         var d = this.getPageData();
19159         if(k == e.RETURN){
19160             var v = this.field.dom.value, pageNum;
19161             if(!v || isNaN(pageNum = parseInt(v, 10))){
19162                 this.field.dom.value = d.activePage;
19163                 return;
19164             }
19165             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19166             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19167             e.stopEvent();
19168         }
19169         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
19170         {
19171           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19172           this.field.dom.value = pageNum;
19173           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19174           e.stopEvent();
19175         }
19176         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19177         {
19178           var v = this.field.dom.value, pageNum; 
19179           var increment = (e.shiftKey) ? 10 : 1;
19180           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19181             increment *= -1;
19182           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19183             this.field.dom.value = d.activePage;
19184             return;
19185           }
19186           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19187           {
19188             this.field.dom.value = parseInt(v, 10) + increment;
19189             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19190             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19191           }
19192           e.stopEvent();
19193         }
19194     },
19195
19196     // private
19197     beforeLoad : function(){
19198         if(this.loading){
19199             this.loading.disable();
19200         }
19201     },
19202
19203     // private
19204     onClick : function(which){
19205         var ds = this.ds;
19206         if (!ds) {
19207             return;
19208         }
19209         switch(which){
19210             case "first":
19211                 ds.load({params:{start: 0, limit: this.pageSize}});
19212             break;
19213             case "prev":
19214                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19215             break;
19216             case "next":
19217                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19218             break;
19219             case "last":
19220                 var total = ds.getTotalCount();
19221                 var extra = total % this.pageSize;
19222                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19223                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19224             break;
19225             case "refresh":
19226                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19227             break;
19228         }
19229     },
19230
19231     /**
19232      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19233      * @param {Roo.data.Store} store The data store to unbind
19234      */
19235     unbind : function(ds){
19236         ds.un("beforeload", this.beforeLoad, this);
19237         ds.un("load", this.onLoad, this);
19238         ds.un("loadexception", this.onLoadError, this);
19239         ds.un("remove", this.updateInfo, this);
19240         ds.un("add", this.updateInfo, this);
19241         this.ds = undefined;
19242     },
19243
19244     /**
19245      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19246      * @param {Roo.data.Store} store The data store to bind
19247      */
19248     bind : function(ds){
19249         ds.on("beforeload", this.beforeLoad, this);
19250         ds.on("load", this.onLoad, this);
19251         ds.on("loadexception", this.onLoadError, this);
19252         ds.on("remove", this.updateInfo, this);
19253         ds.on("add", this.updateInfo, this);
19254         this.ds = ds;
19255     }
19256 });/*
19257  * - LGPL
19258  *
19259  * element
19260  * 
19261  */
19262
19263 /**
19264  * @class Roo.bootstrap.MessageBar
19265  * @extends Roo.bootstrap.Component
19266  * Bootstrap MessageBar class
19267  * @cfg {String} html contents of the MessageBar
19268  * @cfg {String} weight (info | success | warning | danger) default info
19269  * @cfg {String} beforeClass insert the bar before the given class
19270  * @cfg {Boolean} closable (true | false) default false
19271  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19272  * 
19273  * @constructor
19274  * Create a new Element
19275  * @param {Object} config The config object
19276  */
19277
19278 Roo.bootstrap.MessageBar = function(config){
19279     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19280 };
19281
19282 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19283     
19284     html: '',
19285     weight: 'info',
19286     closable: false,
19287     fixed: false,
19288     beforeClass: 'bootstrap-sticky-wrap',
19289     
19290     getAutoCreate : function(){
19291         
19292         var cfg = {
19293             tag: 'div',
19294             cls: 'alert alert-dismissable alert-' + this.weight,
19295             cn: [
19296                 {
19297                     tag: 'span',
19298                     cls: 'message',
19299                     html: this.html || ''
19300                 }
19301             ]
19302         }
19303         
19304         if(this.fixed){
19305             cfg.cls += ' alert-messages-fixed';
19306         }
19307         
19308         if(this.closable){
19309             cfg.cn.push({
19310                 tag: 'button',
19311                 cls: 'close',
19312                 html: 'x'
19313             });
19314         }
19315         
19316         return cfg;
19317     },
19318     
19319     onRender : function(ct, position)
19320     {
19321         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19322         
19323         if(!this.el){
19324             var cfg = Roo.apply({},  this.getAutoCreate());
19325             cfg.id = Roo.id();
19326             
19327             if (this.cls) {
19328                 cfg.cls += ' ' + this.cls;
19329             }
19330             if (this.style) {
19331                 cfg.style = this.style;
19332             }
19333             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19334             
19335             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19336         }
19337         
19338         this.el.select('>button.close').on('click', this.hide, this);
19339         
19340     },
19341     
19342     show : function()
19343     {
19344         if (!this.rendered) {
19345             this.render();
19346         }
19347         
19348         this.el.show();
19349         
19350         this.fireEvent('show', this);
19351         
19352     },
19353     
19354     hide : function()
19355     {
19356         if (!this.rendered) {
19357             this.render();
19358         }
19359         
19360         this.el.hide();
19361         
19362         this.fireEvent('hide', this);
19363     },
19364     
19365     update : function()
19366     {
19367 //        var e = this.el.dom.firstChild;
19368 //        
19369 //        if(this.closable){
19370 //            e = e.nextSibling;
19371 //        }
19372 //        
19373 //        e.data = this.html || '';
19374
19375         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19376     }
19377    
19378 });
19379
19380  
19381
19382      /*
19383  * - LGPL
19384  *
19385  * Graph
19386  * 
19387  */
19388
19389
19390 /**
19391  * @class Roo.bootstrap.Graph
19392  * @extends Roo.bootstrap.Component
19393  * Bootstrap Graph class
19394 > Prameters
19395  -sm {number} sm 4
19396  -md {number} md 5
19397  @cfg {String} graphtype  bar | vbar | pie
19398  @cfg {number} g_x coodinator | centre x (pie)
19399  @cfg {number} g_y coodinator | centre y (pie)
19400  @cfg {number} g_r radius (pie)
19401  @cfg {number} g_height height of the chart (respected by all elements in the set)
19402  @cfg {number} g_width width of the chart (respected by all elements in the set)
19403  @cfg {Object} title The title of the chart
19404     
19405  -{Array}  values
19406  -opts (object) options for the chart 
19407      o {
19408      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19409      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19410      o vgutter (number)
19411      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
19412      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19413      o to
19414      o stretch (boolean)
19415      o }
19416  -opts (object) options for the pie
19417      o{
19418      o cut
19419      o startAngle (number)
19420      o endAngle (number)
19421      } 
19422  *
19423  * @constructor
19424  * Create a new Input
19425  * @param {Object} config The config object
19426  */
19427
19428 Roo.bootstrap.Graph = function(config){
19429     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19430     
19431     this.addEvents({
19432         // img events
19433         /**
19434          * @event click
19435          * The img click event for the img.
19436          * @param {Roo.EventObject} e
19437          */
19438         "click" : true
19439     });
19440 };
19441
19442 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19443     
19444     sm: 4,
19445     md: 5,
19446     graphtype: 'bar',
19447     g_height: 250,
19448     g_width: 400,
19449     g_x: 50,
19450     g_y: 50,
19451     g_r: 30,
19452     opts:{
19453         //g_colors: this.colors,
19454         g_type: 'soft',
19455         g_gutter: '20%'
19456
19457     },
19458     title : false,
19459
19460     getAutoCreate : function(){
19461         
19462         var cfg = {
19463             tag: 'div',
19464             html : null
19465         }
19466         
19467         
19468         return  cfg;
19469     },
19470
19471     onRender : function(ct,position){
19472         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19473         this.raphael = Raphael(this.el.dom);
19474         
19475                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19476                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19477                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19478                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19479                 /*
19480                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19481                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19482                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19483                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19484                 
19485                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19486                 r.barchart(330, 10, 300, 220, data1);
19487                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19488                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19489                 */
19490                 
19491                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19492                 // r.barchart(30, 30, 560, 250,  xdata, {
19493                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19494                 //     axis : "0 0 1 1",
19495                 //     axisxlabels :  xdata
19496                 //     //yvalues : cols,
19497                    
19498                 // });
19499 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19500 //        
19501 //        this.load(null,xdata,{
19502 //                axis : "0 0 1 1",
19503 //                axisxlabels :  xdata
19504 //                });
19505
19506     },
19507
19508     load : function(graphtype,xdata,opts){
19509         this.raphael.clear();
19510         if(!graphtype) {
19511             graphtype = this.graphtype;
19512         }
19513         if(!opts){
19514             opts = this.opts;
19515         }
19516         var r = this.raphael,
19517             fin = function () {
19518                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19519             },
19520             fout = function () {
19521                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19522             },
19523             pfin = function() {
19524                 this.sector.stop();
19525                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19526
19527                 if (this.label) {
19528                     this.label[0].stop();
19529                     this.label[0].attr({ r: 7.5 });
19530                     this.label[1].attr({ "font-weight": 800 });
19531                 }
19532             },
19533             pfout = function() {
19534                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19535
19536                 if (this.label) {
19537                     this.label[0].animate({ r: 5 }, 500, "bounce");
19538                     this.label[1].attr({ "font-weight": 400 });
19539                 }
19540             };
19541
19542         switch(graphtype){
19543             case 'bar':
19544                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19545                 break;
19546             case 'hbar':
19547                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19548                 break;
19549             case 'pie':
19550 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19551 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19552 //            
19553                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19554                 
19555                 break;
19556
19557         }
19558         
19559         if(this.title){
19560             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19561         }
19562         
19563     },
19564     
19565     setTitle: function(o)
19566     {
19567         this.title = o;
19568     },
19569     
19570     initEvents: function() {
19571         
19572         if(!this.href){
19573             this.el.on('click', this.onClick, this);
19574         }
19575     },
19576     
19577     onClick : function(e)
19578     {
19579         Roo.log('img onclick');
19580         this.fireEvent('click', this, e);
19581     }
19582    
19583 });
19584
19585  
19586 /*
19587  * - LGPL
19588  *
19589  * numberBox
19590  * 
19591  */
19592 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19593
19594 /**
19595  * @class Roo.bootstrap.dash.NumberBox
19596  * @extends Roo.bootstrap.Component
19597  * Bootstrap NumberBox class
19598  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19599  * @cfg {String} headline Box headline
19600  * @cfg {String} content Box content
19601  * @cfg {String} icon Box icon
19602  * @cfg {String} footer Footer text
19603  * @cfg {String} fhref Footer href
19604  * 
19605  * @constructor
19606  * Create a new NumberBox
19607  * @param {Object} config The config object
19608  */
19609
19610
19611 Roo.bootstrap.dash.NumberBox = function(config){
19612     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19613     
19614 };
19615
19616 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19617     
19618     bgcolor : 'aqua',
19619     headline : '',
19620     content : '',
19621     icon : '',
19622     footer : '',
19623     fhref : '',
19624     ficon : '',
19625     
19626     getAutoCreate : function(){
19627         
19628         var cfg = {
19629             tag : 'div',
19630             cls : 'small-box bg-' + this.bgcolor,
19631             cn : [
19632                 {
19633                     tag : 'div',
19634                     cls : 'inner',
19635                     cn :[
19636                         {
19637                             tag : 'h3',
19638                             cls : 'roo-headline',
19639                             html : this.headline
19640                         },
19641                         {
19642                             tag : 'p',
19643                             cls : 'roo-content',
19644                             html : this.content
19645                         }
19646                     ]
19647                 }
19648             ]
19649         }
19650         
19651         if(this.icon){
19652             cfg.cn.push({
19653                 tag : 'div',
19654                 cls : 'icon',
19655                 cn :[
19656                     {
19657                         tag : 'i',
19658                         cls : 'ion ' + this.icon
19659                     }
19660                 ]
19661             });
19662         }
19663         
19664         if(this.footer){
19665             var footer = {
19666                 tag : 'a',
19667                 cls : 'small-box-footer',
19668                 href : this.fhref || '#',
19669                 html : this.footer
19670             };
19671             
19672             cfg.cn.push(footer);
19673             
19674         }
19675         
19676         return  cfg;
19677     },
19678
19679     onRender : function(ct,position){
19680         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19681
19682
19683        
19684                 
19685     },
19686
19687     setHeadline: function (value)
19688     {
19689         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19690     },
19691     
19692     setFooter: function (value, href)
19693     {
19694         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19695         
19696         if(href){
19697             this.el.select('a.small-box-footer',true).first().attr('href', href);
19698         }
19699         
19700     },
19701
19702     setContent: function (value)
19703     {
19704         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19705     },
19706
19707     initEvents: function() 
19708     {   
19709         
19710     }
19711     
19712 });
19713
19714  
19715 /*
19716  * - LGPL
19717  *
19718  * TabBox
19719  * 
19720  */
19721 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19722
19723 /**
19724  * @class Roo.bootstrap.dash.TabBox
19725  * @extends Roo.bootstrap.Component
19726  * Bootstrap TabBox class
19727  * @cfg {String} title Title of the TabBox
19728  * @cfg {String} icon Icon of the TabBox
19729  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19730  * 
19731  * @constructor
19732  * Create a new TabBox
19733  * @param {Object} config The config object
19734  */
19735
19736
19737 Roo.bootstrap.dash.TabBox = function(config){
19738     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19739     this.addEvents({
19740         // raw events
19741         /**
19742          * @event addpane
19743          * When a pane is added
19744          * @param {Roo.bootstrap.dash.TabPane} pane
19745          */
19746         "addpane" : true
19747          
19748     });
19749 };
19750
19751 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19752
19753     title : '',
19754     icon : false,
19755     showtabs : true,
19756     
19757     getChildContainer : function()
19758     {
19759         return this.el.select('.tab-content', true).first();
19760     },
19761     
19762     getAutoCreate : function(){
19763         
19764         var header = {
19765             tag: 'li',
19766             cls: 'pull-left header',
19767             html: this.title,
19768             cn : []
19769         };
19770         
19771         if(this.icon){
19772             header.cn.push({
19773                 tag: 'i',
19774                 cls: 'fa ' + this.icon
19775             });
19776         }
19777         
19778         
19779         var cfg = {
19780             tag: 'div',
19781             cls: 'nav-tabs-custom',
19782             cn: [
19783                 {
19784                     tag: 'ul',
19785                     cls: 'nav nav-tabs pull-right',
19786                     cn: [
19787                         header
19788                     ]
19789                 },
19790                 {
19791                     tag: 'div',
19792                     cls: 'tab-content no-padding',
19793                     cn: []
19794                 }
19795             ]
19796         }
19797
19798         return  cfg;
19799     },
19800     initEvents : function()
19801     {
19802         //Roo.log('add add pane handler');
19803         this.on('addpane', this.onAddPane, this);
19804     },
19805      /**
19806      * Updates the box title
19807      * @param {String} html to set the title to.
19808      */
19809     setTitle : function(value)
19810     {
19811         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19812     },
19813     onAddPane : function(pane)
19814     {
19815         //Roo.log('addpane');
19816         //Roo.log(pane);
19817         // tabs are rendere left to right..
19818         if(!this.showtabs){
19819             return;
19820         }
19821         
19822         var ctr = this.el.select('.nav-tabs', true).first();
19823          
19824          
19825         var existing = ctr.select('.nav-tab',true);
19826         var qty = existing.getCount();;
19827         
19828         
19829         var tab = ctr.createChild({
19830             tag : 'li',
19831             cls : 'nav-tab' + (qty ? '' : ' active'),
19832             cn : [
19833                 {
19834                     tag : 'a',
19835                     href:'#',
19836                     html : pane.title
19837                 }
19838             ]
19839         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19840         pane.tab = tab;
19841         
19842         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19843         if (!qty) {
19844             pane.el.addClass('active');
19845         }
19846         
19847                 
19848     },
19849     onTabClick : function(ev,un,ob,pane)
19850     {
19851         //Roo.log('tab - prev default');
19852         ev.preventDefault();
19853         
19854         
19855         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19856         pane.tab.addClass('active');
19857         //Roo.log(pane.title);
19858         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19859         // technically we should have a deactivate event.. but maybe add later.
19860         // and it should not de-activate the selected tab...
19861         
19862         pane.el.addClass('active');
19863         pane.fireEvent('activate');
19864         
19865         
19866     }
19867     
19868     
19869 });
19870
19871  
19872 /*
19873  * - LGPL
19874  *
19875  * Tab pane
19876  * 
19877  */
19878 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19879 /**
19880  * @class Roo.bootstrap.TabPane
19881  * @extends Roo.bootstrap.Component
19882  * Bootstrap TabPane class
19883  * @cfg {Boolean} active (false | true) Default false
19884  * @cfg {String} title title of panel
19885
19886  * 
19887  * @constructor
19888  * Create a new TabPane
19889  * @param {Object} config The config object
19890  */
19891
19892 Roo.bootstrap.dash.TabPane = function(config){
19893     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19894     
19895     this.addEvents({
19896         // raw events
19897         /**
19898          * @event activate
19899          * When a pane is activated
19900          * @param {Roo.bootstrap.dash.TabPane} pane
19901          */
19902         "activate" : true
19903          
19904     });
19905 };
19906
19907 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19908     
19909     active : false,
19910     title : '',
19911     
19912     // the tabBox that this is attached to.
19913     tab : false,
19914      
19915     getAutoCreate : function() 
19916     {
19917         var cfg = {
19918             tag: 'div',
19919             cls: 'tab-pane'
19920         }
19921         
19922         if(this.active){
19923             cfg.cls += ' active';
19924         }
19925         
19926         return cfg;
19927     },
19928     initEvents  : function()
19929     {
19930         //Roo.log('trigger add pane handler');
19931         this.parent().fireEvent('addpane', this)
19932     },
19933     
19934      /**
19935      * Updates the tab title 
19936      * @param {String} html to set the title to.
19937      */
19938     setTitle: function(str)
19939     {
19940         if (!this.tab) {
19941             return;
19942         }
19943         this.title = str;
19944         this.tab.select('a', true).first().dom.innerHTML = str;
19945         
19946     }
19947     
19948     
19949     
19950 });
19951
19952  
19953
19954
19955  /*
19956  * - LGPL
19957  *
19958  * menu
19959  * 
19960  */
19961 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19962
19963 /**
19964  * @class Roo.bootstrap.menu.Menu
19965  * @extends Roo.bootstrap.Component
19966  * Bootstrap Menu class - container for Menu
19967  * @cfg {String} html Text of the menu
19968  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19969  * @cfg {String} icon Font awesome icon
19970  * @cfg {String} pos Menu align to (top | bottom) default bottom
19971  * 
19972  * 
19973  * @constructor
19974  * Create a new Menu
19975  * @param {Object} config The config object
19976  */
19977
19978
19979 Roo.bootstrap.menu.Menu = function(config){
19980     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19981     
19982     this.addEvents({
19983         /**
19984          * @event beforeshow
19985          * Fires before this menu is displayed
19986          * @param {Roo.bootstrap.menu.Menu} this
19987          */
19988         beforeshow : true,
19989         /**
19990          * @event beforehide
19991          * Fires before this menu is hidden
19992          * @param {Roo.bootstrap.menu.Menu} this
19993          */
19994         beforehide : true,
19995         /**
19996          * @event show
19997          * Fires after this menu is displayed
19998          * @param {Roo.bootstrap.menu.Menu} this
19999          */
20000         show : true,
20001         /**
20002          * @event hide
20003          * Fires after this menu is hidden
20004          * @param {Roo.bootstrap.menu.Menu} this
20005          */
20006         hide : true,
20007         /**
20008          * @event click
20009          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20010          * @param {Roo.bootstrap.menu.Menu} this
20011          * @param {Roo.EventObject} e
20012          */
20013         click : true
20014     });
20015     
20016 };
20017
20018 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20019     
20020     submenu : false,
20021     html : '',
20022     weight : 'default',
20023     icon : false,
20024     pos : 'bottom',
20025     
20026     
20027     getChildContainer : function() {
20028         if(this.isSubMenu){
20029             return this.el;
20030         }
20031         
20032         return this.el.select('ul.dropdown-menu', true).first();  
20033     },
20034     
20035     getAutoCreate : function()
20036     {
20037         var text = [
20038             {
20039                 tag : 'span',
20040                 cls : 'roo-menu-text',
20041                 html : this.html
20042             }
20043         ];
20044         
20045         if(this.icon){
20046             text.unshift({
20047                 tag : 'i',
20048                 cls : 'fa ' + this.icon
20049             })
20050         }
20051         
20052         
20053         var cfg = {
20054             tag : 'div',
20055             cls : 'btn-group',
20056             cn : [
20057                 {
20058                     tag : 'button',
20059                     cls : 'dropdown-button btn btn-' + this.weight,
20060                     cn : text
20061                 },
20062                 {
20063                     tag : 'button',
20064                     cls : 'dropdown-toggle btn btn-' + this.weight,
20065                     cn : [
20066                         {
20067                             tag : 'span',
20068                             cls : 'caret'
20069                         }
20070                     ]
20071                 },
20072                 {
20073                     tag : 'ul',
20074                     cls : 'dropdown-menu'
20075                 }
20076             ]
20077             
20078         };
20079         
20080         if(this.pos == 'top'){
20081             cfg.cls += ' dropup';
20082         }
20083         
20084         if(this.isSubMenu){
20085             cfg = {
20086                 tag : 'ul',
20087                 cls : 'dropdown-menu'
20088             }
20089         }
20090         
20091         return cfg;
20092     },
20093     
20094     onRender : function(ct, position)
20095     {
20096         this.isSubMenu = ct.hasClass('dropdown-submenu');
20097         
20098         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20099     },
20100     
20101     initEvents : function() 
20102     {
20103         if(this.isSubMenu){
20104             return;
20105         }
20106         
20107         this.hidden = true;
20108         
20109         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20110         this.triggerEl.on('click', this.onTriggerPress, this);
20111         
20112         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20113         this.buttonEl.on('click', this.onClick, this);
20114         
20115     },
20116     
20117     list : function()
20118     {
20119         if(this.isSubMenu){
20120             return this.el;
20121         }
20122         
20123         return this.el.select('ul.dropdown-menu', true).first();
20124     },
20125     
20126     onClick : function(e)
20127     {
20128         this.fireEvent("click", this, e);
20129     },
20130     
20131     onTriggerPress  : function(e)
20132     {   
20133         if (this.isVisible()) {
20134             this.hide();
20135         } else {
20136             this.show();
20137         }
20138     },
20139     
20140     isVisible : function(){
20141         return !this.hidden;
20142     },
20143     
20144     show : function()
20145     {
20146         this.fireEvent("beforeshow", this);
20147         
20148         this.hidden = false;
20149         this.el.addClass('open');
20150         
20151         Roo.get(document).on("mouseup", this.onMouseUp, this);
20152         
20153         this.fireEvent("show", this);
20154         
20155         
20156     },
20157     
20158     hide : function()
20159     {
20160         this.fireEvent("beforehide", this);
20161         
20162         this.hidden = true;
20163         this.el.removeClass('open');
20164         
20165         Roo.get(document).un("mouseup", this.onMouseUp);
20166         
20167         this.fireEvent("hide", this);
20168     },
20169     
20170     onMouseUp : function()
20171     {
20172         this.hide();
20173     }
20174     
20175 });
20176
20177  
20178  /*
20179  * - LGPL
20180  *
20181  * menu item
20182  * 
20183  */
20184 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20185
20186 /**
20187  * @class Roo.bootstrap.menu.Item
20188  * @extends Roo.bootstrap.Component
20189  * Bootstrap MenuItem class
20190  * @cfg {Boolean} submenu (true | false) default false
20191  * @cfg {String} html text of the item
20192  * @cfg {String} href the link
20193  * @cfg {Boolean} disable (true | false) default false
20194  * @cfg {Boolean} preventDefault (true | false) default true
20195  * @cfg {String} icon Font awesome icon
20196  * @cfg {String} pos Submenu align to (left | right) default right 
20197  * 
20198  * 
20199  * @constructor
20200  * Create a new Item
20201  * @param {Object} config The config object
20202  */
20203
20204
20205 Roo.bootstrap.menu.Item = function(config){
20206     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20207     this.addEvents({
20208         /**
20209          * @event mouseover
20210          * Fires when the mouse is hovering over this menu
20211          * @param {Roo.bootstrap.menu.Item} this
20212          * @param {Roo.EventObject} e
20213          */
20214         mouseover : true,
20215         /**
20216          * @event mouseout
20217          * Fires when the mouse exits this menu
20218          * @param {Roo.bootstrap.menu.Item} this
20219          * @param {Roo.EventObject} e
20220          */
20221         mouseout : true,
20222         // raw events
20223         /**
20224          * @event click
20225          * The raw click event for the entire grid.
20226          * @param {Roo.EventObject} e
20227          */
20228         click : true
20229     });
20230 };
20231
20232 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20233     
20234     submenu : false,
20235     href : '',
20236     html : '',
20237     preventDefault: true,
20238     disable : false,
20239     icon : false,
20240     pos : 'right',
20241     
20242     getAutoCreate : function()
20243     {
20244         var text = [
20245             {
20246                 tag : 'span',
20247                 cls : 'roo-menu-item-text',
20248                 html : this.html
20249             }
20250         ];
20251         
20252         if(this.icon){
20253             text.unshift({
20254                 tag : 'i',
20255                 cls : 'fa ' + this.icon
20256             })
20257         }
20258         
20259         var cfg = {
20260             tag : 'li',
20261             cn : [
20262                 {
20263                     tag : 'a',
20264                     href : this.href || '#',
20265                     cn : text
20266                 }
20267             ]
20268         };
20269         
20270         if(this.disable){
20271             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20272         }
20273         
20274         if(this.submenu){
20275             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20276             
20277             if(this.pos == 'left'){
20278                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20279             }
20280         }
20281         
20282         return cfg;
20283     },
20284     
20285     initEvents : function() 
20286     {
20287         this.el.on('mouseover', this.onMouseOver, this);
20288         this.el.on('mouseout', this.onMouseOut, this);
20289         
20290         this.el.select('a', true).first().on('click', this.onClick, this);
20291         
20292     },
20293     
20294     onClick : function(e)
20295     {
20296         if(this.preventDefault){
20297             e.preventDefault();
20298         }
20299         
20300         this.fireEvent("click", this, e);
20301     },
20302     
20303     onMouseOver : function(e)
20304     {
20305         if(this.submenu && this.pos == 'left'){
20306             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20307         }
20308         
20309         this.fireEvent("mouseover", this, e);
20310     },
20311     
20312     onMouseOut : function(e)
20313     {
20314         this.fireEvent("mouseout", this, e);
20315     }
20316 });
20317
20318  
20319
20320  /*
20321  * - LGPL
20322  *
20323  * menu separator
20324  * 
20325  */
20326 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20327
20328 /**
20329  * @class Roo.bootstrap.menu.Separator
20330  * @extends Roo.bootstrap.Component
20331  * Bootstrap Separator class
20332  * 
20333  * @constructor
20334  * Create a new Separator
20335  * @param {Object} config The config object
20336  */
20337
20338
20339 Roo.bootstrap.menu.Separator = function(config){
20340     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20341 };
20342
20343 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20344     
20345     getAutoCreate : function(){
20346         var cfg = {
20347             tag : 'li',
20348             cls: 'divider'
20349         };
20350         
20351         return cfg;
20352     }
20353    
20354 });
20355
20356  
20357
20358